1use thiserror::Error;
4
5#[derive(Debug, Error)]
10#[non_exhaustive]
11pub enum SpecError {
12 #[error("downstream error: {message}")]
19 Downstream {
20 message: String,
22 #[source]
24 source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
25 },
26 #[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}