feat(tools): h5rs, pure-Rust HDF5 tools (ls, dump, stat, diff, check)
New workspace crate clawhdf5-tools with one binary, h5rs, built only on the clawhdf5 facade and clawhdf5-format (no libhdf5, no C): - ls [-r] [-v] FILE[/path]: h5ls's listing (same text in its first two columns) plus the datatype; -v adds address, link count, layout and chunk index, chunk size, storage, filters, datatype and attributes. - dump [--json] [-A] [-p] [-d PATH] FILE: h5dump DDL (byte-identical to h5dump 1.14.6 on the test files) or hdf5-json. - stat FILE: h5stat's object/link/rank/layout/filter/attribute counts, raw data and total size. - diff [-r] [-q] [-d D] [-p R] A B [OBJ1 [OBJ2]]: structural and value differences, exit 0/1/2 like h5diff. - check [--data] FILE: walks every object, parses every message, verifies the checksums of every v2+ structure (including the fractal heap blocks the library never checks), checks chunk indexes against their datasets and raw data for out-of-file or overlapping extents; every problem with its address. Values over --max-bytes are reported, not read; dense-storage heaps are verified before objects are read from them; panics are caught (exit 3). Tests compare with h5ls, h5stat, h5dump and h5diff and with h5py's values, and flip the checksum of every checksummed structure in a v1.14-format file. Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
//! The `h5rs` binary. A panic anywhere is a bug: it is caught, reported as
|
||||
//! an internal error and turned into exit status 3, never a crash.
|
||||
|
||||
use std::io::Write;
|
||||
use std::panic::{self, AssertUnwindSafe};
|
||||
|
||||
use clawhdf5_tools::cli::Out;
|
||||
|
||||
/// Exit status for an internal error (a caught panic).
|
||||
const INTERNAL_ERROR: i32 = 3;
|
||||
|
||||
fn main() {
|
||||
panic::set_hook(Box::new(|info| {
|
||||
let msg = if let Some(s) = info.payload().downcast_ref::<&str>() {
|
||||
(*s).to_string()
|
||||
} else if let Some(s) = info.payload().downcast_ref::<String>() {
|
||||
s.clone()
|
||||
} else {
|
||||
"unknown panic".into()
|
||||
};
|
||||
let at = info
|
||||
.location()
|
||||
.map(|l| format!(" at {}:{}", l.file(), l.line()))
|
||||
.unwrap_or_default();
|
||||
eprintln!("h5rs: internal error (this is a bug; please report it): {msg}{at}");
|
||||
}));
|
||||
let argv: Vec<String> = std::env::args().skip(1).collect();
|
||||
let stdout = std::io::stdout();
|
||||
let mut o = std::io::BufWriter::new(stdout.lock());
|
||||
let mut e = std::io::stderr();
|
||||
let result = panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
let mut out = Out {
|
||||
o: &mut o,
|
||||
e: &mut e,
|
||||
};
|
||||
clawhdf5_tools::run(argv, &mut out)
|
||||
}));
|
||||
let code = match result {
|
||||
Ok(Ok(code)) => match o.flush() {
|
||||
Ok(()) => code,
|
||||
Err(err) if err.kind() == std::io::ErrorKind::BrokenPipe => code,
|
||||
Err(err) => {
|
||||
eprintln!("h5rs: {err}");
|
||||
2
|
||||
}
|
||||
},
|
||||
Ok(Err(err)) if err.kind() == std::io::ErrorKind::BrokenPipe => 0,
|
||||
Ok(Err(err)) => {
|
||||
eprintln!("h5rs: {err}");
|
||||
2
|
||||
}
|
||||
Err(_) => {
|
||||
let _ = o.flush();
|
||||
INTERNAL_ERROR
|
||||
}
|
||||
};
|
||||
std::process::exit(code);
|
||||
}
|
||||
Reference in New Issue
Block a user