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 parameter bound was NaN.
63 #[error("invalid NaN bound {value} for {name}")]
64 InvalidBoundValue {
65 /// Parameter name.
66 name: String,
67 /// Invalid bound value.
68 value: f64,
69 },
70 /// A uniform initial range had its endpoints reversed.
71 #[error("invalid uniform initial range for {name}: min {min} is greater than max {max}")]
72 InvalidInitialRange {
73 /// Parameter name.
74 name: String,
75 /// Invalid range minimum.
76 min: f64,
77 /// Invalid range maximum.
78 max: f64,
79 },
80 /// A parameter's initial value fell outside its bounds.
81 #[error("initial value {value} for {name} is outside bounds")]
82 InitialOutOfBounds {
83 /// Parameter name.
84 name: String,
85 /// Invalid initial value.
86 value: f64,
87 },
88 /// A parameter's initial range extended outside its bounds.
89 #[error("initial range [{min}, {max}] for {name} is outside parameter bounds")]
90 InitialRangeOutOfBounds {
91 /// Parameter name.
92 name: String,
93 /// Initial range minimum.
94 min: f64,
95 /// Initial range maximum.
96 max: f64,
97 },
98 /// A scalar initial value was not finite.
99 #[error("initial value {value} for {name} must be finite")]
100 NonFiniteInitialValue {
101 /// Parameter name.
102 name: String,
103 /// Invalid initial value.
104 value: f64,
105 },
106 /// An initial range endpoint was not finite.
107 #[error("initial range [{min}, {max}] for {name} must be finite")]
108 NonFiniteInitialRange {
109 /// Parameter name.
110 name: String,
111 /// Range minimum.
112 min: f64,
113 /// Range maximum.
114 max: f64,
115 },
116 /// A fixed parameter value fell outside its bounds.
117 #[error("fixed value {value} for {name} is outside bounds")]
118 FixedValueOutOfBounds {
119 /// Parameter name.
120 name: String,
121 /// Invalid fixed value.
122 value: f64,
123 },
124 /// A fixed parameter value was not finite.
125 #[error("fixed value {value} for {name} must be finite")]
126 NonFiniteFixedValue {
127 /// Parameter name.
128 name: String,
129 /// Invalid fixed value.
130 value: f64,
131 },
132 /// An assigned parameter value fell outside its bounds.
133 #[error("value {value} for {name} is outside bounds")]
134 ValueOutOfBounds {
135 /// Parameter name.
136 name: String,
137 /// Invalid assigned value.
138 value: f64,
139 },
140 /// A periodic parameter did not have finite, ordered bounds.
141 #[error("periodic parameter {name} requires finite two-sided bounds with min < max")]
142 PeriodicRequiresFiniteBounds {
143 /// Parameter name.
144 name: String,
145 },
146 /// A parameter scale was not finite and positive.
147 #[error("invalid scale for {name}: expected a finite positive value, got {scale}")]
148 InvalidScale {
149 /// Parameter name.
150 name: String,
151 /// Invalid scale.
152 scale: f64,
153 },
154 /// A fixed value in a parameter update was not finite.
155 #[error("parameter update fixed value must be finite, got {value}")]
156 InvalidUpdateFixedValue {
157 /// Invalid fixed value.
158 value: f64,
159 },
160 /// A scalar initial value in a parameter update was not finite.
161 #[error("parameter update initial value must be finite, got {value}")]
162 InvalidUpdateInitialValue {
163 /// Invalid initial value.
164 value: f64,
165 },
166 /// A uniform initial range in a parameter update was not finite or ordered.
167 #[error("parameter update initial range must be finite and ordered, got [{min}, {max}]")]
168 InvalidUpdateInitialRange {
169 /// Invalid range minimum.
170 min: f64,
171 /// Invalid range maximum.
172 max: f64,
173 },
174 /// Bounds in a parameter update were unordered or contained NaN.
175 #[error(
176 "parameter update bounds must be ordered and cannot contain NaN, got [{min:?}, {max:?}]"
177 )]
178 InvalidUpdateBounds {
179 /// Invalid lower bound.
180 min: Option<f64>,
181 /// Invalid upper bound.
182 max: Option<f64>,
183 },
184 /// A scale in a parameter update was not finite and positive.
185 #[error("parameter update scale must be finite and positive, got {scale}")]
186 InvalidUpdateScale {
187 /// Invalid scale.
188 scale: f64,
189 },
190 /// A periodic value was outside its canonical half-open domain.
191 #[error(
192 "value {value} for periodic parameter {name} is outside canonical domain [{min}, {max})"
193 )]
194 ValueOutsidePeriodicDomain {
195 /// Parameter name.
196 name: String,
197 /// Invalid value.
198 value: f64,
199 /// Inclusive domain minimum.
200 min: f64,
201 /// Exclusive domain maximum.
202 max: f64,
203 },
204}
205
206/// Structural validation errors for serialized expression graphs.
207#[derive(Clone, Debug, Error, PartialEq, Eq)]
208pub enum ExprGraphError {
209 /// The node and metadata arrays had different lengths.
210 #[error("graph metadata length {metadata_len} does not match node length {node_len}")]
211 MetadataLength {
212 /// Number of graph nodes.
213 node_len: usize,
214 /// Number of metadata entries.
215 metadata_len: usize,
216 },
217 /// The root identifier was outside the node array.
218 #[error("graph root node #{root} is out of bounds for graph with {node_len} nodes")]
219 InvalidRoot {
220 /// Invalid root index.
221 root: usize,
222 /// Number of graph nodes.
223 node_len: usize,
224 },
225 /// A node referenced a child outside the node array.
226 #[error("graph node #{node} references missing child #{child}")]
227 InvalidChild {
228 /// Parent node index.
229 node: usize,
230 /// Invalid child index.
231 child: usize,
232 },
233 /// A node referenced a child stored after its parent.
234 #[error(
235 "graph node #{node} references child #{child}, but children must appear before parents"
236 )]
237 InvalidChildOrder {
238 /// Parent node index.
239 node: usize,
240 /// Out-of-order child index.
241 child: usize,
242 },
243 /// The graph contained no nodes.
244 #[error("graph is empty")]
245 Empty,
246}
247
248/// Describes an operation applied to an expression with an incompatible shape.
249#[derive(Clone, Debug, Error, PartialEq, Eq)]
250#[error("invalid expression shape for {operation}: {message}")]
251pub struct ExprShapeError {
252 operation: &'static str,
253 message: String,
254}
255
256impl ExprShapeError {
257 pub(crate) fn new(operation: &'static str, message: impl Into<String>) -> Self {
258 Self {
259 operation,
260 message: message.into(),
261 }
262 }
263}
264
265/// Errors produced while constructing or rebuilding expressions.
266#[derive(Clone, Debug, Error, PartialEq)]
267pub enum ExprError {
268 /// A parameter operation failed.
269 #[error(transparent)]
270 Params(#[from] ParamError),
271 /// A serialized graph was structurally invalid.
272 #[error(transparent)]
273 Graph(#[from] ExprGraphError),
274 /// An operation received incompatible expression shapes.
275 #[error(transparent)]
276 Shape(#[from] ExprShapeError),
277}