Skip to main content

laddu_expr/
error.rs

1use thiserror::Error;
2
3/// Result type for parameter construction and lookup operations.
4pub type ParamResult<T> = Result<T, ParamError>;
5/// Result type for expression construction and validation operations.
6pub type ExprResult<T> = Result<T, ExprError>;
7
8/// Errors produced while defining, laying out, or assigning parameters.
9#[derive(Clone, Debug, Error, PartialEq)]
10pub enum ParamError {
11    /// A parameter name was empty.
12    #[error("parameter name cannot be empty")]
13    EmptyName,
14    #[error("duplicate parameter name: {0}")]
15    /// A parameter name was registered more than once.
16    DuplicateName(String),
17    #[error("unknown parameter: {0}")]
18    /// A parameter name was not present in the layout.
19    UnknownName(String),
20    #[error("parameter conflict for {name}: {reason}")]
21    /// Two definitions of the same parameter were incompatible.
22    ParameterConflict {
23        /// Conflicting parameter name.
24        name: String,
25        /// Description of the incompatibility.
26        reason: String,
27    },
28    /// A parameter identifier was outside the layout.
29    #[error("invalid parameter id #{id} for layout of size {len}")]
30    InvalidParamId {
31        /// Invalid identifier index.
32        id: usize,
33        /// Number of parameters in the layout.
34        len: usize,
35    },
36    /// A free-parameter identifier was outside the layout.
37    #[error("invalid free parameter id #{id} for layout with {len} free parameters")]
38    InvalidFreeParamId {
39        /// Invalid free-parameter index.
40        id: usize,
41        /// Number of free parameters in the layout.
42        len: usize,
43    },
44    /// A free-parameter value vector had the wrong length.
45    #[error("expected {expected} free parameters, got {actual}")]
46    FreeLengthMismatch {
47        /// Required number of values.
48        expected: usize,
49        /// Supplied number of values.
50        actual: usize,
51    },
52    /// A parameter's lower bound exceeded its upper bound.
53    #[error("invalid bounds for {name}: min {min} is greater than max {max}")]
54    InvalidBounds {
55        /// Parameter name.
56        name: String,
57        /// Invalid lower bound.
58        min: f64,
59        /// Invalid upper bound.
60        max: f64,
61    },
62    /// A uniform initial range had its endpoints reversed.
63    #[error("invalid uniform initial range for {name}: min {min} is greater than max {max}")]
64    InvalidInitialRange {
65        /// Parameter name.
66        name: String,
67        /// Invalid range minimum.
68        min: f64,
69        /// Invalid range maximum.
70        max: f64,
71    },
72    /// A parameter's initial value fell outside its bounds.
73    #[error("initial value {value} for {name} is outside bounds")]
74    InitialOutOfBounds {
75        /// Parameter name.
76        name: String,
77        /// Invalid initial value.
78        value: f64,
79    },
80    /// A parameter's initial range extended outside its bounds.
81    #[error("initial range [{min}, {max}] for {name} is outside parameter bounds")]
82    InitialRangeOutOfBounds {
83        /// Parameter name.
84        name: String,
85        /// Initial range minimum.
86        min: f64,
87        /// Initial range maximum.
88        max: f64,
89    },
90    /// A fixed parameter value fell outside its bounds.
91    #[error("fixed value {value} for {name} is outside bounds")]
92    FixedValueOutOfBounds {
93        /// Parameter name.
94        name: String,
95        /// Invalid fixed value.
96        value: f64,
97    },
98    /// An assigned parameter value fell outside its bounds.
99    #[error("value {value} for {name} is outside bounds")]
100    ValueOutOfBounds {
101        /// Parameter name.
102        name: String,
103        /// Invalid assigned value.
104        value: f64,
105    },
106    /// A periodic parameter did not have finite, ordered bounds.
107    #[error("periodic parameter {name} requires finite two-sided bounds with min < max")]
108    PeriodicRequiresFiniteBounds {
109        /// Parameter name.
110        name: String,
111    },
112    /// A parameter scale was not finite and positive.
113    #[error("invalid scale for {name}: expected a finite positive value, got {scale}")]
114    InvalidScale {
115        /// Parameter name.
116        name: String,
117        /// Invalid scale.
118        scale: f64,
119    },
120    /// A periodic value was outside its canonical half-open domain.
121    #[error(
122        "value {value} for periodic parameter {name} is outside canonical domain [{min}, {max})"
123    )]
124    ValueOutsidePeriodicDomain {
125        /// Parameter name.
126        name: String,
127        /// Invalid value.
128        value: f64,
129        /// Inclusive domain minimum.
130        min: f64,
131        /// Exclusive domain maximum.
132        max: f64,
133    },
134}
135
136/// Structural validation errors for serialized expression graphs.
137#[derive(Clone, Debug, Error, PartialEq, Eq)]
138pub enum ExprGraphError {
139    /// The node and metadata arrays had different lengths.
140    #[error("graph metadata length {metadata_len} does not match node length {node_len}")]
141    MetadataLength {
142        /// Number of graph nodes.
143        node_len: usize,
144        /// Number of metadata entries.
145        metadata_len: usize,
146    },
147    /// The root identifier was outside the node array.
148    #[error("graph root node #{root} is out of bounds for graph with {node_len} nodes")]
149    InvalidRoot {
150        /// Invalid root index.
151        root: usize,
152        /// Number of graph nodes.
153        node_len: usize,
154    },
155    /// A node referenced a child outside the node array.
156    #[error("graph node #{node} references missing child #{child}")]
157    InvalidChild {
158        /// Parent node index.
159        node: usize,
160        /// Invalid child index.
161        child: usize,
162    },
163    /// A node referenced a child stored after its parent.
164    #[error(
165        "graph node #{node} references child #{child}, but children must appear before parents"
166    )]
167    InvalidChildOrder {
168        /// Parent node index.
169        node: usize,
170        /// Out-of-order child index.
171        child: usize,
172    },
173    /// The graph contained no nodes.
174    #[error("graph is empty")]
175    Empty,
176}
177
178/// Describes an operation applied to an expression with an incompatible shape.
179#[derive(Clone, Debug, Error, PartialEq, Eq)]
180#[error("invalid expression shape for {operation}: {message}")]
181pub struct ExprShapeError {
182    operation: &'static str,
183    message: String,
184}
185
186impl ExprShapeError {
187    pub(crate) fn new(operation: &'static str, message: impl Into<String>) -> Self {
188        Self {
189            operation,
190            message: message.into(),
191        }
192    }
193}
194
195/// Errors produced while constructing or rebuilding expressions.
196#[derive(Clone, Debug, Error, PartialEq)]
197pub enum ExprError {
198    /// A parameter operation failed.
199    #[error(transparent)]
200    Params(#[from] ParamError),
201    /// A serialized graph was structurally invalid.
202    #[error(transparent)]
203    Graph(#[from] ExprGraphError),
204    /// An operation received incompatible expression shapes.
205    #[error(transparent)]
206    Shape(#[from] ExprShapeError),
207}