Skip to main content

hm_exec/
error.rs

1//! The error type returned across the `ExecutionBackend` boundary.
2
3/// A backend failure returned across the `ExecutionBackend` boundary.
4///
5/// Distinguishes *infrastructure* failures (return `Err`) from a *failed
6/// build* (`Ok(BuildOutcome { status: Failed, .. })`). `#[non_exhaustive]`
7/// so new backends can add variants without breaking callers.
8#[derive(Debug, thiserror::Error)]
9#[non_exhaustive]
10pub enum BackendError {
11    #[error("authentication required")]
12    Unauthorized,
13    #[error("backend rejected the build [{code}]: {message}")]
14    Rejected { code: String, message: String },
15    #[error("not found: {0}")]
16    NotFound(String),
17    #[error("network error: {0}")]
18    Transport(String),
19    #[error("log stream error: {0}")]
20    LogStream(String),
21    #[error("local execution error: {0}")]
22    Local(String),
23    /// The worktree archive exceeds the upload cap. Carries the observed
24    /// (compressed) size, the cap, and a human hint naming the largest
25    /// top-level paths so the user can `.gitignore` the offenders. Fails fast
26    /// BEFORE the upload (see the cloud backend's `start`).
27    #[error("source archive is {observed_bytes} bytes (cap {cap_bytes})")]
28    SourceTooLarge {
29        observed_bytes: u64,
30        cap_bytes: u64,
31        largest_paths: Vec<(String, u64)>,
32    },
33    #[error(transparent)]
34    Other(#[from] Box<dyn std::error::Error + Send + Sync>),
35}
36
37pub type Result<T> = std::result::Result<T, BackendError>;
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    #[test]
44    fn unauthorized_is_matchable_and_displayed() {
45        let e = BackendError::Unauthorized;
46        assert!(matches!(e, BackendError::Unauthorized));
47        assert!(e.to_string().contains("authentication"));
48    }
49
50    #[test]
51    fn rejected_carries_code_and_message() {
52        let e = BackendError::Rejected {
53            code: "build_rejected".into(),
54            message: "bad IR".into(),
55        };
56        let s = e.to_string();
57        assert!(s.contains("build_rejected") && s.contains("bad IR"));
58    }
59}