Skip to main content

klieo_spec/
error.rs

1//! Crate-level error type.
2
3use thiserror::Error;
4
5/// Errors raised by `klieo-spec` traits and the loop runner.
6///
7/// Marked `#[non_exhaustive]` so additional variants can be introduced
8/// without a major-version bump on impl crates that match on the enum.
9#[derive(Debug, Error)]
10#[non_exhaustive]
11pub enum SpecError {
12    /// A downstream `Decomposer`/`Critic`/`Refiner` impl failed.
13    ///
14    /// Carries a context message plus the impl's original error as
15    /// `#[source]` so `e.source()` traversal preserves the cause chain.
16    /// Covers any downstream trait-impl failure that does not fit the
17    /// typed variants.
18    #[error("downstream error: {message}")]
19    Downstream {
20        /// Human-readable context describing the wrap site.
21        message: String,
22        /// Underlying error preserved for `std::error::Error::source()`.
23        #[source]
24        source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
25    },
26    /// A misconfiguration in the loop (e.g. zero max iterations).
27    #[error("config error: {0}")]
28    Config(String),
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34
35    use std::error::Error as StdError;
36
37    #[test]
38    fn downstream_message_surfaces_through_display() {
39        let e = SpecError::Downstream {
40            message: "refiner: empty result".into(),
41            source: None,
42        };
43        assert_eq!(e.to_string(), "downstream error: refiner: empty result");
44    }
45
46    #[test]
47    fn downstream_preserves_source_chain() {
48        let inner = std::io::Error::other("network down");
49        let e = SpecError::Downstream {
50            message: "critic call failed".into(),
51            source: Some(Box::new(inner)),
52        };
53        let src = e.source().expect("source must be preserved");
54        assert_eq!(src.to_string(), "network down");
55    }
56
57    #[test]
58    fn downstream_without_source_returns_none() {
59        let e = SpecError::Downstream {
60            message: "no inner".into(),
61            source: None,
62        };
63        assert!(e.source().is_none());
64    }
65}