1use thiserror::Error;
4
5#[derive(Debug, Error)]
7#[non_exhaustive]
8pub enum BuildError {
9 #[error("primitive `{needed_by}` requires `{missing}` to be configured")]
11 MissingDependency {
12 needed_by: String,
14 missing: String,
16 },
17}
18
19#[derive(Debug, Error)]
21#[non_exhaustive]
22pub enum OpsError {
23 #[error("denied: {code}")]
25 Denied {
26 code: String,
28 reason: Option<String>,
30 },
31 #[error("require approval")]
33 RequireApproval,
34 #[error("timed out at phase `{phase}`")]
36 TimedOut {
37 phase: String,
39 },
40 #[error("saturated: {scope}")]
42 Saturated {
43 scope: String,
45 },
46 #[error("halted")]
48 Halted,
49 #[error("unavailable: {component}")]
51 Unavailable {
52 component: String,
54 },
55 #[error("integrity failed: {reasons}")]
57 IntegrityFailed {
58 reasons: String,
60 },
61 #[error(transparent)]
63 Build(#[from] BuildError),
64 #[error("internal: {source}")]
66 Internal {
67 source: Box<dyn std::error::Error + Send + Sync>,
69 },
70}
71
72#[cfg(test)]
73mod tests {
74 use super::*;
75
76 #[test]
77 fn build_error_displays_missing_dependency() {
78 let e = BuildError::MissingDependency {
79 needed_by: "gates".into(),
80 missing: "escalation".into(),
81 };
82 let s = format!("{e}");
83 assert!(s.contains("gates"));
84 assert!(s.contains("escalation"));
85 }
86
87 #[test]
88 fn ops_error_halted_is_distinct_from_unavailable() {
89 let a = OpsError::Halted;
90 let b = OpsError::Unavailable {
91 component: "kv".into(),
92 };
93 assert_ne!(format!("{a}"), format!("{b}"));
94 }
95}