Skip to main content

sim_lib_interference_solve/
scenario_error.rs

1//! Stable diagnostics for bounded named scenario construction.
2
3use std::fmt;
4
5use sim_lib_interference_core::InterferenceError;
6
7/// A named scenario was refused before returning a partial problem.
8#[derive(Clone, Debug, PartialEq)]
9pub enum ScenarioError {
10    /// A configured limit was zero or exceeded its absolute safety ceiling.
11    InvalidLimit {
12        /// Stable limit name.
13        name: &'static str,
14        /// Rejected value.
15        value: usize,
16        /// Absolute maximum.
17        absolute_maximum: usize,
18    },
19    /// An array or aperture dimension was zero.
20    ZeroElementDimension {
21        /// Stable dimension name.
22        name: &'static str,
23    },
24    /// Rectangular source count overflowed `usize`.
25    SourceCountOverflow {
26        /// Requested aperture rows.
27        rows: usize,
28        /// Requested aperture columns.
29        columns: usize,
30    },
31    /// Source count exceeded the configured limit.
32    SourceLimitExceeded {
33        /// Requested source count.
34        requested: usize,
35        /// Configured maximum.
36        limit: usize,
37    },
38    /// One generated source id would exceed its configured byte limit.
39    GeneratedIdLimitExceeded {
40        /// Required bytes in the longest identity.
41        requested: usize,
42        /// Configured maximum.
43        limit: usize,
44    },
45    /// Aggregate generated id storage would exceed its configured byte limit.
46    TotalIdLimitExceeded {
47        /// Required aggregate bytes.
48        requested: usize,
49        /// Configured maximum.
50        limit: usize,
51    },
52    /// Strict policy rejected an active element spacing above `lambda / 2`.
53    SparseAperture {
54        /// Stable aperture axis (`u` or `v`).
55        axis: &'static str,
56        /// Rejected spacing in wavelengths.
57        spacing_wavelengths: f64,
58        /// Maximum strict spacing.
59        maximum_wavelengths: f64,
60    },
61    /// A derived geometric or phase value was invalid.
62    Core {
63        /// Exact checked-domain diagnostic.
64        cause: Box<InterferenceError>,
65    },
66    /// Source-vector storage could not be reserved after admission.
67    AllocationFailed {
68        /// Admitted source count.
69        sources: usize,
70    },
71}
72
73impl From<InterferenceError> for ScenarioError {
74    fn from(cause: InterferenceError) -> Self {
75        Self::Core {
76            cause: Box::new(cause),
77        }
78    }
79}
80
81impl fmt::Display for ScenarioError {
82    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
83        match self {
84            Self::InvalidLimit {
85                name,
86                value,
87                absolute_maximum,
88            } => write!(
89                formatter,
90                "scenario limit `{name}` must be in 1..={absolute_maximum}: {value}"
91            ),
92            Self::ZeroElementDimension { name } => {
93                write!(
94                    formatter,
95                    "scenario element dimension `{name}` must be non-zero"
96                )
97            }
98            Self::SourceCountOverflow { rows, columns } => write!(
99                formatter,
100                "scenario source count overflows usize: {rows} rows by {columns} columns"
101            ),
102            Self::SourceLimitExceeded { requested, limit } => write!(
103                formatter,
104                "scenario requests {requested} sources but the limit is {limit}"
105            ),
106            Self::GeneratedIdLimitExceeded { requested, limit } => write!(
107                formatter,
108                "scenario generated id requires {requested} bytes but the limit is {limit}"
109            ),
110            Self::TotalIdLimitExceeded { requested, limit } => write!(
111                formatter,
112                "scenario generated ids require {requested} bytes but the limit is {limit}"
113            ),
114            Self::SparseAperture {
115                axis,
116                spacing_wavelengths,
117                maximum_wavelengths,
118            } => write!(
119                formatter,
120                "strict aperture spacing on `{axis}` is {spacing_wavelengths:?} wavelengths, \
121                 above {maximum_wavelengths:?}"
122            ),
123            Self::Core { cause } => write!(formatter, "scenario input is invalid: {cause}"),
124            Self::AllocationFailed { sources } => {
125                write!(
126                    formatter,
127                    "could not reserve storage for {sources} scenario sources"
128                )
129            }
130        }
131    }
132}
133
134impl std::error::Error for ScenarioError {
135    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
136        match self {
137            Self::Core { cause } => Some(cause.as_ref()),
138            _ => None,
139        }
140    }
141}