Skip to main content

zsfm_hub/
log.rs

1/// Status logging that respects `ZSFM_QUIET`.
2///
3/// All human-readable progress goes to stderr so `infer` JSON on stdout stays
4/// pipe-clean. Set `ZSFM_QUIET=1` (or pass `zsfm --quiet`) to silence status
5/// lines; errors still print.
6pub fn log_status(msg: &str) {
7    if std::env::var_os("ZSFM_QUIET").is_some() {
8        return;
9    }
10    eprintln!("{msg}");
11}
12
13/// Typed Hub errors. Functions still return `anyhow::Result` at the boundary
14/// so callers keep one error type, but raise these variants so messages stay
15/// consistent and matchable via `.to_string()`.
16#[derive(Debug, thiserror::Error)]
17pub enum HubError {
18    #[error(
19        "resume offset mismatch for {relpath}: local has {local} bytes, server starts at {server}"
20    )]
21    ResumeMismatch {
22        relpath: String,
23        local: u64,
24        server: u64,
25    },
26    #[error("size mismatch for {relpath}: server said {expected} bytes, got {actual}")]
27    SizeMismatch {
28        relpath: String,
29        expected: u64,
30        actual: u64,
31    },
32    #[error("shard filename collision after flattening: {0}")]
33    ShardCollision(String),
34    #[error("repo listing too large: {0} bytes")]
35    ListingTooLarge(usize),
36}
37
38#[cfg(test)]
39mod tests {
40    use super::*;
41
42    #[test]
43    fn messages() {
44        let e = HubError::SizeMismatch {
45            relpath: "model.safetensors".into(),
46            expected: 10,
47            actual: 9,
48        };
49        assert!(e.to_string().contains("size mismatch"));
50    }
51}