jaq_interpret/
error.rs

1//! Runtime errors.
2use crate::val::{Val, ValT};
3use alloc::string::{String, ToString};
4use core::fmt;
5
6/// Errors that can occur during filter execution.
7///
8/// Each variant shows an example of how it can be produced.
9#[derive(Clone, Debug, PartialEq, Eq)]
10#[non_exhaustive]
11pub enum Error<V = Val> {
12    /// `0 | error`
13    Val(V),
14
15    /// Expected a value of given type, but got something else
16    Type(V, Type),
17    /// `1 - "a"`
18    MathOp(V, jaq_syn::MathOp, V),
19    /// `{} | .[0]` or `[] | has("a")` or `{} | has(0)`
20    Index(V, V),
21
22    /// `[] | .[0] = 0`
23    IndexOutOfBounds(isize),
24    /// `0 |= .+1`
25    PathExp,
26
27    /// Tail-recursive call.
28    ///
29    /// This is used internally to execute tail-recursive filters.
30    /// If this can be observed by users, then this is a bug.
31    TailCall(crate::filter::TailCall<V>),
32}
33
34/// Types and sets of types.
35#[derive(Clone, Debug, PartialEq, Eq)]
36#[non_exhaustive]
37pub enum Type {
38    /// `[] | .["a"]` or `limit("a"; 0)` or `range(0; "a")`
39    Int,
40    /// `"1" | sin` or `pow(2; "3")` or `fma(2; 3; "4")`
41    Float,
42    /// `-"a"`, `"a" | round`
43    Num,
44    /// `{(0): 1}` or `0 | fromjson` or `0 | explode` or `"a b c" | split(0)`
45    Str,
46    /// `0 | sort` or `0 | implode` or `[] | .[0:] = 0`
47    Arr,
48    /// `0 | .[]` or `0 | .[0]` or `0 | keys` (array or object)
49    Iter,
50    /// `{}[0:1]` (string or array)
51    Range,
52}
53
54impl<V: ValT> Error<V> {
55    /// Convert the error into a value to be used by `catch` filters.
56    pub fn as_val(self) -> V {
57        match self {
58            Self::Val(ev) => ev,
59            _ => V::from(self.to_string()),
60        }
61    }
62}
63
64impl<V: From<String>> Error<V> {
65    /// Build an error from something that can be converted to a string.
66    pub fn str(s: impl ToString) -> Self {
67        Self::Val(V::from(s.to_string()))
68    }
69}
70
71impl<V: ValT> fmt::Display for Error<V> {
72    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
73        match self {
74            Self::Val(v) => {
75                if let Some(s) = v.as_str() {
76                    write!(f, "{s}")
77                } else {
78                    write!(f, "{v}")
79                }
80            }
81            Self::Type(v, ty) => write!(f, "cannot use {v} as {ty}"),
82            Self::MathOp(l, op, r) => write!(f, "cannot calculate {l} {op} {r}"),
83            Self::Index(v, i) => write!(f, "cannot index {v} with {i}"),
84            Self::IndexOutOfBounds(i) => write!(f, "index {i} is out of bounds"),
85            Self::PathExp => write!(f, "invalid path expression"),
86            Self::TailCall(_) => panic!(),
87        }
88    }
89}
90
91#[cfg(feature = "std")]
92impl std::error::Error for Error {}
93
94impl fmt::Display for Type {
95    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96        match self {
97            Self::Int => "integer".fmt(f),
98            Self::Float => "floating-point number".fmt(f),
99            Self::Num => "number".fmt(f),
100            Self::Str => "string".fmt(f),
101            Self::Arr => "array".fmt(f),
102            Self::Iter => "iterable (array or object)".fmt(f),
103            Self::Range => "rangeable (array or string)".fmt(f),
104        }
105    }
106}