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 thiserror::Error as ThisError;
9
10/// A problem instance failure.
11#[derive(Debug, ThisError)]
12#[non_exhaustive]
13pub enum Error {
14    /// A failure from the balanced model, its readers, or its writers.
15    #[error(transparent)]
16    Core(#[from] powerio::Error),
17
18    /// A failure from the matrix and dataset builders.
19    #[cfg(feature = "matrix")]
20    #[error(transparent)]
21    Matrix(#[from] powerio_matrix::Error),
22
23    /// An underlying I/O failure reading or writing a file.
24    #[error(transparent)]
25    Io(#[from] std::io::Error),
26
27    #[error("case has no generators; DC-OPF requires an `mpc.gen` block")]
28    NoGenerators,
29
30    #[error(
31        "generator {gen_index} has an unsupported cost model (model {model}, ncost {ncost}); need polynomial model 2 with degree ≤ 2"
32    )]
33    UnsupportedCostModel {
34        gen_index: usize,
35        model: u8,
36        ncost: usize,
37    },
38}
39
40impl Error {
41    /// Classify this error, using the hub's taxonomy.
42    ///
43    /// The match is exhaustive over the variant set, so a new variant must be
44    /// classified here before it compiles.
45    #[must_use]
46    pub fn category(&self) -> powerio::ErrorCategory {
47        use powerio::ErrorCategory as C;
48        match self {
49            Error::Core(inner) => inner.category(),
50            #[cfg(feature = "matrix")]
51            Error::Matrix(inner) => inner.category(),
52            Error::Io(_) => C::Io,
53            // A well-formed case that cannot satisfy a requested operation.
54            Error::NoGenerators | Error::UnsupportedCostModel { .. } => C::Data,
55        }
56    }
57}
58
59/// The result type every fallible entry point in this crate returns.
60pub type Result<T> = std::result::Result<T, Error>;
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65    use powerio::ErrorCategory::{Data, Parse};
66
67    #[test]
68    fn category_pins_the_intended_buckets() {
69        assert_eq!(Error::NoGenerators.category(), Data);
70        assert_eq!(
71            Error::UnsupportedCostModel {
72                gen_index: 0,
73                model: 1,
74                ncost: 4
75            }
76            .category(),
77            Data
78        );
79    }
80
81    #[test]
82    fn a_wrapped_hub_error_keeps_its_own_category_and_message() {
83        let wrapped: Error = powerio::Error::MissingField("gen").into();
84        assert_eq!(wrapped.category(), Parse);
85        assert_eq!(
86            wrapped.to_string(),
87            powerio::Error::MissingField("gen").to_string()
88        );
89    }
90}