1use thiserror::Error;
16
17#[derive(Debug, Clone, Error)]
19pub enum XPathError {
20 #[error("[XPST0003] Syntax error: {message}")]
25 XPST0003 { message: String },
26
27 #[error("[XPST0008] QName '{qname}' is not defined")]
29 XPST0008 { qname: String },
30
31 #[error("[XPST0051] The type name '{type_name}' is not defined as an atomic type")]
33 XPST0051 { type_name: String },
34
35 #[error("[XPST0017] Function '{name}/{arity}' not found in namespace '{namespace}'")]
37 XPST0017 {
38 name: String,
39 arity: usize,
40 namespace: String,
41 },
42
43 #[error("[XPST0081] Prefix '{prefix}' cannot be expanded to a namespace URI")]
45 XPST0081 { prefix: String },
46
47 #[error("[XPDY0002] {message}")]
52 XPDY0002 { message: String },
53
54 #[error("[XPDY0050] More than one item in sequence where single item expected")]
56 XPDY0050,
57
58 #[error("[XPTY0004] Type mismatch: expected '{expected}', found '{found}'")]
63 XPTY0004 { expected: String, found: String },
64
65 #[error("[XPTY0004] Only string literals can be cast to type '{target_type}'")]
67 XPTY0004Cast { target_type: String },
68
69 #[error("[XPTY0018] Path expression result contains both nodes and atomic values")]
71 XPTY0018,
72
73 #[error("[XPTY0019] Step result in path expression must not be an atomic value")]
75 XPTY0019,
76
77 #[error("[XPTY0020] Context item for axis step is not a node")]
79 XPTY0020,
80
81 #[error(
86 "[XQTY0030] Validate expression argument must be exactly one document or element node"
87 )]
88 XQTY0030,
89
90 #[error("[XQST0076] Collation '{collation}' is not supported")]
95 XQST0076 { collation: String },
96
97 #[error("[FORG0001] Invalid value '{value}' for cast to type '{target_type}'")]
102 FORG0001 { value: String, target_type: String },
103
104 #[error("[FORG0003] fn:zero-or-one called with sequence containing more than one item")]
106 FORG0003,
107
108 #[error("[FORG0004] fn:one-or-more called with empty sequence")]
110 FORG0004,
111
112 #[error(
114 "[FORG0005] fn:exactly-one called with sequence containing zero or more than one item"
115 )]
116 FORG0005,
117
118 #[error("[FORG0006] Function '{function}' called with invalid argument type '{arg_type}'")]
120 FORG0006Named { function: String, arg_type: String },
121
122 #[error("[FORG0006] {message}")]
124 FORG0006 { message: String },
125
126 #[error("[FORG0008] Both arguments to fn:dateTime have a timezone")]
128 FORG0008,
129
130 #[error("[FOCH0001] Invalid codepoint '{codepoint}' in codepoints-to-string")]
135 FOCH0001 { codepoint: String },
136
137 #[error("[FOCH0002] Unsupported collation: '{collation}'")]
139 FOCH0002 { collation: String },
140
141 #[error("[FOCH0003] Unsupported normalization form '{normalization_form}'")]
143 FOCH0003 { normalization_form: String },
144
145 #[error("[FOAR0001] Division by zero")]
150 FOAR0001,
151
152 #[error("[FOAR0002] Numeric operation overflow/underflow")]
154 FOAR0002,
155
156 #[error("[FOCA0002] QName '{qname}' has null namespace but non-empty prefix")]
161 FOCA0002 { qname: String },
162
163 #[error("[FOCA0003] {message}")]
165 FOCA0003 { message: String },
166
167 #[error("[FOCA0005] NaN supplied as float/double value")]
169 FOCA0005,
170
171 #[error("[FODT0001] Overflow/underflow in date/time operation")]
176 FODT0001,
177
178 #[error("[FODT0002] Overflow/underflow in duration operation")]
180 FODT0002,
181
182 #[error("[FODT0003] Invalid timezone value: {value}")]
184 FODT0003 { value: String },
185
186 #[error("[FOTY0012] Argument node does not have a typed value")]
191 FOTY0012,
192
193 #[error("[FONS0005] Base-uri not defined in the static context")]
198 FONS0005,
199
200 #[error("[FORG0009] Error in resolving a relative URI against a base URI: '{uri}'")]
205 FORG0009 { uri: String },
206
207 #[error("[FORX0001] Invalid regular expression flags: '{flags}'")]
212 FORX0001 { flags: String },
213
214 #[error("[FORX0002] Invalid regular expression: '{pattern}'")]
216 FORX0002 { pattern: String },
217
218 #[error("[FORX0003] Regular expression matches zero-length string: '{pattern}'")]
220 FORX0003 { pattern: String },
221
222 #[error("[FORX0004] Invalid replacement string: '{replacement}'")]
224 FORX0004 { replacement: String },
225
226 #[error("Operator '{operator}' is not defined for arguments of type '{left_type}' and '{right_type}'")]
231 BinaryOperatorNotDefined {
232 operator: String,
233 left_type: String,
234 right_type: String,
235 },
236
237 #[error("Operator '{operator}' is not defined for argument of type '{arg_type}'")]
239 UnaryOperatorNotDefined { operator: String, arg_type: String },
240
241 #[error("XPath error: {0}")]
246 Internal(String),
247}
248
249impl From<crate::xpath::parser::ParseError> for XPathError {
250 fn from(e: crate::xpath::parser::ParseError) -> Self {
251 XPathError::XPST0003 {
252 message: e.to_string(),
253 }
254 }
255}
256
257impl From<crate::navigator::NavigatorError> for XPathError {
258 fn from(e: crate::navigator::NavigatorError) -> Self {
259 XPathError::Internal(e.to_string())
260 }
261}
262
263impl XPathError {
264 pub fn internal(message: impl Into<String>) -> Self {
270 XPathError::Internal(message.into())
271 }
272
273 pub fn syntax_error(message: impl Into<String>) -> Self {
275 XPathError::XPST0003 {
276 message: message.into(),
277 }
278 }
279
280 pub fn undefined_qname(qname: impl Into<String>) -> Self {
282 XPathError::XPST0008 {
283 qname: qname.into(),
284 }
285 }
286
287 pub fn unknown_type(type_name: impl Into<String>) -> Self {
289 XPathError::XPST0051 {
290 type_name: type_name.into(),
291 }
292 }
293
294 pub fn function_not_found(
296 name: impl Into<String>,
297 arity: usize,
298 namespace: impl Into<String>,
299 ) -> Self {
300 XPathError::XPST0017 {
301 name: name.into(),
302 arity,
303 namespace: namespace.into(),
304 }
305 }
306
307 pub fn undefined_prefix(prefix: impl Into<String>) -> Self {
309 XPathError::XPST0081 {
310 prefix: prefix.into(),
311 }
312 }
313
314 pub fn context_undefined() -> Self {
316 XPathError::XPDY0002 {
317 message: "The context item is undefined".to_string(),
318 }
319 }
320
321 pub fn more_than_one_item() -> Self {
323 XPathError::XPDY0050
324 }
325
326 pub fn type_mismatch(expected: impl Into<String>, found: impl Into<String>) -> Self {
328 XPathError::XPTY0004 {
329 expected: expected.into(),
330 found: found.into(),
331 }
332 }
333
334 pub fn cast_requires_string_literal(target_type: impl Into<String>) -> Self {
336 XPathError::XPTY0004Cast {
337 target_type: target_type.into(),
338 }
339 }
340
341 pub fn context_not_a_node() -> Self {
343 XPathError::XPTY0020
344 }
345
346 pub fn invalid_cast_value(value: impl Into<String>, target_type: impl Into<String>) -> Self {
348 XPathError::FORG0001 {
349 value: value.into(),
350 target_type: target_type.into(),
351 }
352 }
353
354 pub fn invalid_argument_type(function: impl Into<String>, arg_type: impl Into<String>) -> Self {
356 XPathError::FORG0006Named {
357 function: function.into(),
358 arg_type: arg_type.into(),
359 }
360 }
361
362 pub fn not_implemented(message: impl Into<String>) -> Self {
364 XPathError::Internal(format!("Not implemented: {}", message.into()))
365 }
366
367 pub fn wrong_number_of_arguments(function: &str, expected: usize, actual: usize) -> Self {
369 XPathError::XPST0017 {
370 name: function.to_string(),
371 arity: actual,
372 namespace: format!(
373 "(expected {} argument{})",
374 expected,
375 if expected == 1 { "" } else { "s" }
376 ),
377 }
378 }
379
380 pub fn binary_operator_not_defined(
382 operator: impl Into<String>,
383 left_type: impl Into<String>,
384 right_type: impl Into<String>,
385 ) -> Self {
386 XPathError::BinaryOperatorNotDefined {
387 operator: operator.into(),
388 left_type: left_type.into(),
389 right_type: right_type.into(),
390 }
391 }
392
393 pub fn unary_operator_not_defined(
395 operator: impl Into<String>,
396 arg_type: impl Into<String>,
397 ) -> Self {
398 XPathError::UnaryOperatorNotDefined {
399 operator: operator.into(),
400 arg_type: arg_type.into(),
401 }
402 }
403
404 pub fn no_typed_value() -> Self {
406 XPathError::FOTY0012
407 }
408
409 pub fn base_uri_not_defined() -> Self {
411 XPathError::FONS0005
412 }
413
414 pub fn uri_resolution_error(uri: impl Into<String>) -> Self {
416 XPathError::FORG0009 { uri: uri.into() }
417 }
418
419 pub fn invalid_regex_flags(flags: impl Into<String>) -> Self {
421 XPathError::FORX0001 {
422 flags: flags.into(),
423 }
424 }
425
426 pub fn invalid_regex_pattern(pattern: impl Into<String>) -> Self {
428 XPathError::FORX0002 {
429 pattern: pattern.into(),
430 }
431 }
432
433 pub fn regex_matches_zero_length(pattern: impl Into<String>) -> Self {
435 XPathError::FORX0003 {
436 pattern: pattern.into(),
437 }
438 }
439
440 pub fn invalid_replacement_string(replacement: impl Into<String>) -> Self {
442 XPathError::FORX0004 {
443 replacement: replacement.into(),
444 }
445 }
446
447 pub fn unsupported_collation(collation: impl Into<String>) -> Self {
449 XPathError::XQST0076 {
450 collation: collation.into(),
451 }
452 }
453
454 pub fn unknown_collation(collation: impl Into<String>) -> Self {
456 XPathError::FOCH0002 {
457 collation: collation.into(),
458 }
459 }
460
461 pub fn error_code(&self) -> Option<&'static str> {
463 match self {
464 XPathError::XPST0003 { .. } => Some("XPST0003"),
465 XPathError::XPST0008 { .. } => Some("XPST0008"),
466 XPathError::XPST0051 { .. } => Some("XPST0051"),
467 XPathError::XPST0017 { .. } => Some("XPST0017"),
468 XPathError::XPST0081 { .. } => Some("XPST0081"),
469 XPathError::XPDY0002 { .. } => Some("XPDY0002"),
470 XPathError::XPDY0050 => Some("XPDY0050"),
471 XPathError::XPTY0004 { .. } => Some("XPTY0004"),
472 XPathError::XPTY0004Cast { .. } => Some("XPTY0004"),
473 XPathError::XPTY0018 => Some("XPTY0018"),
474 XPathError::XPTY0019 => Some("XPTY0019"),
475 XPathError::XPTY0020 => Some("XPTY0020"),
476 XPathError::XQTY0030 => Some("XQTY0030"),
477 XPathError::XQST0076 { .. } => Some("XQST0076"),
478 XPathError::FORG0001 { .. } => Some("FORG0001"),
479 XPathError::FORG0003 => Some("FORG0003"),
480 XPathError::FORG0004 => Some("FORG0004"),
481 XPathError::FORG0005 => Some("FORG0005"),
482 XPathError::FORG0006Named { .. } => Some("FORG0006"),
483 XPathError::FORG0006 { .. } => Some("FORG0006"),
484 XPathError::FORG0008 => Some("FORG0008"),
485 XPathError::FOCH0001 { .. } => Some("FOCH0001"),
486 XPathError::FOCH0002 { .. } => Some("FOCH0002"),
487 XPathError::FOCH0003 { .. } => Some("FOCH0003"),
488 XPathError::FOAR0001 => Some("FOAR0001"),
489 XPathError::FOAR0002 => Some("FOAR0002"),
490 XPathError::FOCA0002 { .. } => Some("FOCA0002"),
491 XPathError::FOCA0003 { .. } => Some("FOCA0003"),
492 XPathError::FOCA0005 => Some("FOCA0005"),
493 XPathError::FODT0001 => Some("FODT0001"),
494 XPathError::FODT0002 => Some("FODT0002"),
495 XPathError::FODT0003 { .. } => Some("FODT0003"),
496 XPathError::FOTY0012 => Some("FOTY0012"),
497 XPathError::FONS0005 => Some("FONS0005"),
498 XPathError::FORG0009 { .. } => Some("FORG0009"),
499 XPathError::FORX0001 { .. } => Some("FORX0001"),
500 XPathError::FORX0002 { .. } => Some("FORX0002"),
501 XPathError::FORX0003 { .. } => Some("FORX0003"),
502 XPathError::FORX0004 { .. } => Some("FORX0004"),
503 XPathError::BinaryOperatorNotDefined { .. } => None,
504 XPathError::UnaryOperatorNotDefined { .. } => None,
505 XPathError::Internal(_) => None,
506 }
507 }
508}
509
510#[cfg(test)]
511mod tests {
512 use super::*;
513
514 #[test]
515 fn test_error_codes() {
516 assert_eq!(
517 XPathError::context_undefined().error_code(),
518 Some("XPDY0002")
519 );
520 assert_eq!(
521 XPathError::more_than_one_item().error_code(),
522 Some("XPDY0050")
523 );
524 assert_eq!(
525 XPathError::type_mismatch("xs:integer", "xs:string").error_code(),
526 Some("XPTY0004")
527 );
528 }
529
530 #[test]
531 fn test_error_display() {
532 let err = XPathError::type_mismatch("xs:integer", "xs:string");
533 assert!(err.to_string().contains("XPTY0004"));
534 assert!(err.to_string().contains("xs:integer"));
535 assert!(err.to_string().contains("xs:string"));
536 }
537
538 #[test]
539 fn test_internal_error() {
540 let err = XPathError::internal("something went wrong");
541 assert!(err.to_string().contains("something went wrong"));
542 assert_eq!(err.error_code(), None);
543 }
544}