Skip to main content

jql_runner/
errors.rs

1use jql_parser::errors::JqlParserError;
2use serde_json::Value;
3use thiserror::Error;
4
5static SLICE_SEP: &str = " ... ";
6static SLICE_LEN: usize = 7;
7static SEP: &str = ", ";
8
9/// Joins multiple `String`.
10fn join(values: &[String]) -> String {
11    values.join(SEP)
12}
13
14/// Shortens a JSON `Value` for error injection.
15fn shorten(json: &Value) -> String {
16    let full_json_string = json.to_string();
17
18    if full_json_string.len() < SLICE_LEN * 2 + SLICE_SEP.len() {
19        return full_json_string;
20    }
21
22    let start_slice = &full_json_string[..SLICE_LEN];
23    let end_slice = &full_json_string[full_json_string.len() - SLICE_LEN..];
24
25    [start_slice, end_slice].join(SLICE_SEP)
26}
27
28/// Returns the type of a JSON `Value`.
29fn get_json_type(json: &Value) -> &str {
30    match json {
31        Value::Array(_) => "array",
32        Value::Bool(_) => "boolean",
33        Value::Null => "null",
34        Value::Number(_) => "number",
35        Value::Object(_) => "object",
36        Value::String(_) => "string",
37    }
38}
39
40/// Error type returned by the runner.
41///
42/// Marked `#[non_exhaustive]`: new variants are added as the runner grows, and
43/// downstream code must not break when one appears.
44#[derive(Debug, Error, PartialEq)]
45#[non_exhaustive]
46pub enum JqlRunnerError {
47    /// Deserialization error.
48    #[error("Failed to deserialize the JSON data")]
49    DeserializationError,
50
51    /// Empty query error.
52    #[error("Query is empty")]
53    EmptyQueryError,
54
55    /// Flatten error.
56    #[error("Value {0} is neither an array nor an object and can't be flattened")]
57    FlattenError(Value),
58
59    /// Index out of bounds error.
60    #[error("Index {index} in parent {parent} is out of bounds")]
61    IndexOutOfBoundsError {
62        /// Index.
63        index: usize,
64        /// Parent value.
65        parent: Value,
66    },
67
68    /// Invalid array error.
69    #[error("Value {} is not a JSON array ({})", .0, get_json_type(.0))]
70    InvalidArrayError(Value),
71
72    /// Invalid object error.
73    #[error("Value {} is not a JSON object ({})", .0, get_json_type(.0))]
74    InvalidObjectError(Value),
75
76    /// Key not found error.
77    #[error(r#"Key "{key}" doesn't exist in parent {}"#, shorten(parent))]
78    KeyNotFoundError {
79        /// Key not found.
80        key: String,
81        /// Parent value.
82        parent: Value,
83    },
84
85    /// Keys not found error.
86    #[error("Keys {} don't exist in parent {}", join(keys), shorten(parent))]
87    MultiKeyNotFoundError {
88        /// Keys not found.
89        keys: Vec<String>,
90        /// Parent value.
91        parent: Value,
92    },
93
94    /// Parsing error.
95    #[error(transparent)]
96    ParsingError(#[from] JqlParserError),
97
98    /// Pipe in error.
99    #[error("Pipe in operator used on {0} which is not an array")]
100    PipeInError(Value),
101
102    /// Pipe in error.
103    #[error("Pipe out operator used without a preceding pipe in operator")]
104    PipeOutError,
105
106    /// Range out of bounds error.
107    #[error("Range [{start}:{end}] in parent {parent} is out of bounds")]
108    RangeOutOfBoundsError {
109        /// Start range.
110        start: usize,
111        /// End range.
112        end: usize,
113        /// parent value.
114        parent: Value,
115    },
116
117    /// Unknown error.
118    #[error("Unknown error")]
119    UnknownError,
120}
121
122#[cfg(test)]
123mod tests {
124
125    use serde_json::json;
126
127    use super::{
128        get_json_type,
129        join,
130        shorten,
131    };
132
133    #[test]
134    fn check_get_json_type() {
135        assert_eq!(get_json_type(&json!([])), "array");
136        assert_eq!(get_json_type(&json!(true)), "boolean");
137        assert_eq!(get_json_type(&json!(null)), "null");
138        assert_eq!(get_json_type(&json!(1)), "number");
139        assert_eq!(get_json_type(&json!({})), "object");
140        assert_eq!(get_json_type(&json!("a")), "string");
141    }
142
143    #[test]
144    fn check_join() {
145        assert_eq!(
146            join(&["a".to_string(), "b".to_string(), "c".to_string()]),
147            "a, b, c".to_string()
148        );
149    }
150
151    #[test]
152    fn check_shorten() {
153        assert_eq!(shorten(&json!("thismakesnosense")), r#""thismakesnosense""#);
154        assert_eq!(
155            shorten(&json!({ "a": { "b": { "c": [1, 2 ,3, 4, 5, 6, 7, 8, 9] } } })),
156            r#"{"a":{" ... 8,9]}}}"#.to_string()
157        );
158    }
159}