1use std::error::Error as StdError;
8use thiserror::Error;
9
10#[derive(Debug, Error)]
15pub enum GateError {
16 #[error("Invalid geometry: {message}")]
18 InvalidGeometry { message: String },
19
20 #[error("Missing parameter '{parameter}' in context: {context}")]
22 MissingParameter { parameter: String, context: String },
23
24 #[error("Invalid coordinate '{coordinate}': value {value} is not finite or out of range")]
26 InvalidCoordinate { coordinate: String, value: f32 },
27
28 #[error("Filtering error: {message}")]
30 FilteringError { message: String },
31
32 #[error("Hierarchy error: {message}")]
34 HierarchyError { message: String },
35
36 #[error("Serialization error: {0}")]
38 SerializationError(#[from] serde_json::Error),
39
40 #[error("Index error: {message}")]
42 IndexError { message: String },
43
44 #[error("{message}")]
46 Other {
47 message: String,
48 #[source]
49 source: Option<Box<dyn StdError + Send + Sync>>,
50 },
51
52 #[error("Hierarchy cycle detected: adding '{gate_id}' as parent of '{would_create_cycle_to}' would create a cycle")]
54 HierarchyCycle {
55 gate_id: String,
56 would_create_cycle_to: String,
57 },
58
59 #[error("Invalid boolean operation '{operation}': expected {expected_count} operand(s), got {operand_count}")]
61 InvalidBooleanOperation {
62 operation: String,
63 operand_count: usize,
64 expected_count: usize,
65 },
66
67 #[error("Gate '{gate_id}' not found: {context}")]
69 GateNotFound {
70 gate_id: String,
71 context: String,
72 },
73
74 #[error("Invalid link from '{linking_gate_id}' to '{target_gate_id}': {reason}")]
76 InvalidLink {
77 target_gate_id: String,
78 linking_gate_id: String,
79 reason: String,
80 },
81
82 #[error("Cannot reparent gate '{gate_id}' to '{new_parent_id}': {reason}")]
84 CannotReparent {
85 gate_id: String,
86 new_parent_id: String,
87 reason: String,
88 },
89
90 #[error("Invalid subtree operation '{operation}' on gate '{gate_id}': {reason}")]
92 InvalidSubtreeOperation {
93 gate_id: String,
94 operation: String,
95 reason: String,
96 },
97
98 #[error("Boolean operation '{operation}' requires at least one operand")]
100 EmptyOperands {
101 operation: String,
102 },
103
104 #[error("Builder field '{field}' is invalid: {reason}")]
106 InvalidBuilderState {
107 field: String,
108 reason: String,
109 },
110
111 #[error("Gate ID '{gate_id}' already exists")]
113 DuplicateGateId {
114 gate_id: String,
115 },
116}
117
118impl GateError {
119 pub fn invalid_geometry(message: impl Into<String>) -> Self {
121 Self::InvalidGeometry {
122 message: message.into(),
123 }
124 }
125
126 pub fn missing_parameter(parameter: impl Into<String>, context: impl Into<String>) -> Self {
128 Self::MissingParameter {
129 parameter: parameter.into(),
130 context: context.into(),
131 }
132 }
133
134 pub fn invalid_coordinate(coordinate: impl Into<String>, value: f32) -> Self {
136 Self::InvalidCoordinate {
137 coordinate: coordinate.into(),
138 value,
139 }
140 }
141
142 pub fn filtering_error(message: impl Into<String>) -> Self {
144 Self::FilteringError {
145 message: message.into(),
146 }
147 }
148
149 pub fn hierarchy_error(message: impl Into<String>) -> Self {
151 Self::HierarchyError {
152 message: message.into(),
153 }
154 }
155
156 pub fn index_error(message: impl Into<String>) -> Self {
158 Self::IndexError {
159 message: message.into(),
160 }
161 }
162
163 pub fn hierarchy_cycle(gate_id: impl Into<String>, would_create_cycle_to: impl Into<String>) -> Self {
165 Self::HierarchyCycle {
166 gate_id: gate_id.into(),
167 would_create_cycle_to: would_create_cycle_to.into(),
168 }
169 }
170
171 pub fn invalid_boolean_operation(
173 operation: impl Into<String>,
174 operand_count: usize,
175 expected_count: usize,
176 ) -> Self {
177 Self::InvalidBooleanOperation {
178 operation: operation.into(),
179 operand_count,
180 expected_count,
181 }
182 }
183
184 pub fn gate_not_found(gate_id: impl Into<String>, context: impl Into<String>) -> Self {
186 Self::GateNotFound {
187 gate_id: gate_id.into(),
188 context: context.into(),
189 }
190 }
191
192 pub fn invalid_link(
194 target_gate_id: impl Into<String>,
195 linking_gate_id: impl Into<String>,
196 reason: impl Into<String>,
197 ) -> Self {
198 Self::InvalidLink {
199 target_gate_id: target_gate_id.into(),
200 linking_gate_id: linking_gate_id.into(),
201 reason: reason.into(),
202 }
203 }
204
205 pub fn cannot_reparent(
207 gate_id: impl Into<String>,
208 new_parent_id: impl Into<String>,
209 reason: impl Into<String>,
210 ) -> Self {
211 Self::CannotReparent {
212 gate_id: gate_id.into(),
213 new_parent_id: new_parent_id.into(),
214 reason: reason.into(),
215 }
216 }
217
218 pub fn invalid_subtree_operation(
220 gate_id: impl Into<String>,
221 operation: impl Into<String>,
222 reason: impl Into<String>,
223 ) -> Self {
224 Self::InvalidSubtreeOperation {
225 gate_id: gate_id.into(),
226 operation: operation.into(),
227 reason: reason.into(),
228 }
229 }
230
231 pub fn empty_operands(operation: impl Into<String>) -> Self {
233 Self::EmptyOperands {
234 operation: operation.into(),
235 }
236 }
237
238 pub fn invalid_builder_state(field: impl Into<String>, reason: impl Into<String>) -> Self {
240 Self::InvalidBuilderState {
241 field: field.into(),
242 reason: reason.into(),
243 }
244 }
245
246 pub fn duplicate_gate_id(gate_id: impl Into<String>) -> Self {
248 Self::DuplicateGateId {
249 gate_id: gate_id.into(),
250 }
251 }
252
253 pub fn with_context(self, context: impl Into<String>) -> Self {
255 match self {
256 Self::InvalidGeometry { message } => Self::InvalidGeometry {
257 message: format!("{}: {}", context.into(), message),
258 },
259 Self::MissingParameter {
260 parameter,
261 context: ctx,
262 } => Self::MissingParameter {
263 parameter,
264 context: format!("{}: {}", context.into(), ctx),
265 },
266 Self::InvalidCoordinate { coordinate, value } => {
267 Self::InvalidCoordinate { coordinate, value }
268 }
269 Self::FilteringError { message } => Self::FilteringError {
270 message: format!("{}: {}", context.into(), message),
271 },
272 Self::HierarchyError { message } => Self::HierarchyError {
273 message: format!("{}: {}", context.into(), message),
274 },
275 Self::SerializationError(e) => Self::Other {
276 message: format!("{}: {}", context.into(), e),
277 source: Some(Box::new(e)),
278 },
279 Self::IndexError { message } => Self::IndexError {
280 message: format!("{}: {}", context.into(), message),
281 },
282 Self::HierarchyCycle { gate_id, would_create_cycle_to } => Self::HierarchyCycle {
283 gate_id,
284 would_create_cycle_to,
285 },
286 Self::InvalidBooleanOperation { operation, operand_count, expected_count } => {
287 Self::InvalidBooleanOperation {
288 operation,
289 operand_count,
290 expected_count,
291 }
292 }
293 Self::GateNotFound { gate_id, context: ctx } => Self::GateNotFound {
294 gate_id,
295 context: format!("{}: {}", context.into(), ctx),
296 },
297 Self::InvalidLink { target_gate_id, linking_gate_id, reason } => Self::InvalidLink {
298 target_gate_id,
299 linking_gate_id,
300 reason: format!("{}: {}", context.into(), reason),
301 },
302 Self::CannotReparent { gate_id, new_parent_id, reason } => Self::CannotReparent {
303 gate_id,
304 new_parent_id,
305 reason: format!("{}: {}", context.into(), reason),
306 },
307 Self::InvalidSubtreeOperation { gate_id, operation, reason } => {
308 Self::InvalidSubtreeOperation {
309 gate_id,
310 operation,
311 reason: format!("{}: {}", context.into(), reason),
312 }
313 }
314 Self::EmptyOperands { operation } => Self::EmptyOperands { operation },
315 Self::InvalidBuilderState { field, reason } => Self::InvalidBuilderState {
316 field,
317 reason: format!("{}: {}", context.into(), reason),
318 },
319 Self::DuplicateGateId { gate_id } => Self::DuplicateGateId { gate_id },
320 Self::Other { message, source } => Self::Other {
321 message: format!("{}: {}", context.into(), message),
322 source,
323 },
324 }
325 }
326}
327
328impl From<anyhow::Error> for GateError {
330 fn from(err: anyhow::Error) -> Self {
331 Self::Other {
332 message: err.to_string(),
333 source: None, }
335 }
336}
337
338impl From<quick_xml::Error> for GateError {
340 fn from(err: quick_xml::Error) -> Self {
341 Self::Other {
342 message: format!("XML parsing error: {}", err),
343 source: Some(Box::new(err)),
344 }
345 }
346}
347
348impl From<std::io::Error> for GateError {
350 fn from(err: std::io::Error) -> Self {
351 Self::Other {
352 message: format!("IO error: {}", err),
353 source: Some(Box::new(err)),
354 }
355 }
356}
357
358pub type Result<T> = std::result::Result<T, GateError>;