Skip to main content

powerio_prob/
error.rs

1//! Failures the problem instance builders raise.
2//!
3//! [`Error`] carries what this crate constructs and wraps the crates beneath
4//! it, so `?` moves a failure across the boundary without restating it. A
5//! caller that only wants the coarse split reads [`Error::category`], which is
6//! the same taxonomy every powerio surface uses.
7
8use powerio_core::DiagnosticInfo;
9use thiserror::Error as ThisError;
10
11use crate::diagnostics::codes;
12
13/// A problem instance failure.
14#[derive(Debug, ThisError)]
15#[non_exhaustive]
16pub enum Error {
17    /// A failure from the balanced model, its readers, or its writers.
18    #[error(transparent)]
19    Transmission(#[from] powerio_tx::Error),
20
21    /// An underlying I/O failure reading or writing a file.
22    #[error(transparent)]
23    Io(#[from] std::io::Error),
24}
25
26impl Error {
27    /// The registry entry for this error. The match is exhaustive over the
28    /// variant set, so a new variant must be coded here before it compiles.
29    #[must_use]
30    pub fn code(&self) -> &'static DiagnosticInfo {
31        match self {
32            Error::Transmission(inner) => inner.code(),
33            Error::Io(_) => &codes::READ_INSTANCE_IO_FAILED,
34        }
35    }
36
37    /// Classify this error, using the hub's taxonomy.
38    ///
39    /// The match is exhaustive over the variant set, so a new variant must be
40    /// classified here before it compiles.
41    #[must_use]
42    pub fn category(&self) -> powerio_tx::ErrorCategory {
43        use powerio_tx::ErrorCategory as C;
44        match self {
45            Error::Transmission(inner) => inner.category(),
46            Error::Io(_) => C::Io,
47        }
48    }
49}
50
51/// The result type every fallible entry point in this crate returns.
52pub type Result<T> = std::result::Result<T, Error>;
53
54#[cfg(test)]
55mod tests {
56    use super::*;
57    use powerio_tx::ErrorCategory::Parse;
58
59    // Every error is a diagnostic that ended the operation, so the code's
60    // published category and `category()` are one fact.
61    #[test]
62    fn every_error_code_publishes_the_category_the_variant_reports() {
63        let every: Vec<Error> = vec![
64            powerio_tx::Error::MissingField("gen").into(),
65            std::io::Error::from(std::io::ErrorKind::NotFound).into(),
66        ];
67        for error in &every {
68            assert_eq!(
69                error.code().category,
70                Some(error.category()),
71                "{}",
72                error.code().code
73            );
74        }
75    }
76
77    #[test]
78    fn a_wrapped_hub_error_keeps_its_own_category_and_message() {
79        let wrapped: Error = powerio_tx::Error::MissingField("gen").into();
80        assert_eq!(wrapped.category(), Parse);
81        assert_eq!(
82            wrapped.to_string(),
83            powerio_tx::Error::MissingField("gen").to_string()
84        );
85    }
86}