use thiserror::Error;
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum SpecError {
#[error("downstream error: {message}")]
Downstream {
message: String,
#[source]
source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
},
#[deprecated(
since = "0.7.0",
note = "renamed to SpecError::Downstream — Impl will be removed in 0.8"
)]
#[error("trait error: {0}")]
Impl(String),
#[error("max iterations reached ({iterations})")]
MaxIterations {
iterations: u8,
},
#[error("config error: {0}")]
Config(String),
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error as StdError;
#[test]
fn downstream_message_surfaces_through_display() {
let e = SpecError::Downstream {
message: "refiner: empty result".into(),
source: None,
};
assert_eq!(e.to_string(), "downstream error: refiner: empty result");
}
#[test]
fn downstream_preserves_source_chain() {
let inner = std::io::Error::other("network down");
let e = SpecError::Downstream {
message: "critic call failed".into(),
source: Some(Box::new(inner)),
};
let src = e.source().expect("source must be preserved");
assert_eq!(src.to_string(), "network down");
}
#[test]
fn downstream_without_source_returns_none() {
let e = SpecError::Downstream {
message: "no inner".into(),
source: None,
};
assert!(e.source().is_none());
}
#[test]
#[allow(deprecated)]
fn impl_variant_still_compiles_as_deprecated_alias() {
let e = SpecError::Impl("legacy caller".to_string());
assert!(e.to_string().contains("trait error"));
}
#[test]
fn max_iterations_display() {
let e = SpecError::MaxIterations { iterations: 3 };
assert_eq!(e.to_string(), "max iterations reached (3)");
}
}