jql_runner/
runner.rs

1use jql_parser::{
2    group::split,
3    parser::parse,
4    tokens::Token,
5};
6use rayon::prelude::*;
7use serde_json::{
8    Value,
9    json,
10};
11
12use crate::{
13    array::{
14        get_array_as_indexes,
15        get_array_indexes,
16        get_array_lenses,
17        get_array_range,
18        get_flattened_array,
19    },
20    errors::JqlRunnerError,
21    object::{
22        get_flattened_object,
23        get_object_as_keys,
24        get_object_indexes,
25        get_object_key,
26        get_object_multi_key,
27        get_object_range,
28    },
29};
30
31/// Takes a raw input as a slice string to parse and a reference of a JSON
32/// `Value`.
33/// Returns a JSON `Value`.
34///
35/// # Errors
36///
37/// Returns a `JqlRunnerError` on failure.
38pub fn raw(input: &str, json: &Value) -> Result<Value, JqlRunnerError> {
39    if input.is_empty() {
40        return Err(JqlRunnerError::EmptyQueryError);
41    }
42
43    let tokens = parse(input)?;
44
45    token(&tokens, json)
46}
47
48/// Takes a slice of `Tokens` to parse and a reference of a JSON
49/// `Value`.
50/// Returns a JSON `Value`.
51///
52/// # Errors
53///
54/// Returns a `JqlRunnerError` on failure.
55pub fn token(tokens: &[Token], json: &Value) -> Result<Value, JqlRunnerError> {
56    let groups = split(tokens);
57
58    let result = groups
59        .par_iter()
60        .try_fold_with(vec![], |mut acc: Vec<Value>, group| {
61            acc.push(group_runner(group, json)?);
62
63            Ok::<Vec<Value>, JqlRunnerError>(acc)
64        })
65        .try_reduce(Vec::new, |mut a, b| {
66            a.extend(b);
67
68            Ok(a)
69        });
70
71    result.map(|group| {
72        if groups.len() == 1 {
73            json!(group[0])
74        } else {
75            json!(group)
76        }
77    })
78}
79
80/// Takes a slice of references of `Token` and a reference of a JSON `Value`.
81/// Returns a JSON `Value` or an error.
82/// Note: the `GroupSeparator` enum variant is unreachable at this point since
83/// it has been filtered out by any of the public `runner` functions.
84pub(crate) fn group_runner(tokens: &[&Token], json: &Value) -> Result<Value, JqlRunnerError> {
85    tokens
86        .iter()
87        // At this level we can use rayon since every token is applied
88        // sequentially.
89        .try_fold((json.clone(), false), |mut outer_acc, &token| {
90            if outer_acc.1 {
91                let result = outer_acc
92                    .0
93                    .as_array_mut()
94                    // We can safely unwrap since `outer_acc.1` is truthy.
95                    .unwrap()
96                    .par_iter()
97                    .try_fold_with(
98                        (vec![], outer_acc.1),
99                        |mut inner_acc: (Vec<Value>, bool), inner_value| {
100                            let result = matcher((inner_value.clone(), outer_acc.1), token)?;
101
102                            inner_acc.0.push(result.0);
103                            inner_acc.1 = result.1;
104
105                            Ok::<(Vec<Value>, bool), JqlRunnerError>(inner_acc)
106                        },
107                    )
108                    .try_reduce(
109                        || (vec![], false),
110                        |mut a, b| {
111                            a.0.extend(b.0);
112
113                            Ok((a.0, b.1))
114                        },
115                    )?;
116
117                Ok((json!(result.0), result.1))
118            } else {
119                matcher(outer_acc, token)
120            }
121        })
122        // Drop the `pipe` boolean flag.
123        .map(|(value, _)| value)
124}
125
126/// Internal matcher consumed by the `group_runner` to apply a selection based
127/// on the provided mutable JSON `Value` and the reference of a `Token`.
128/// A `piped` flag is used to keep track of the pipe operators.
129fn matcher(
130    (mut acc, mut piped): (Value, bool),
131    token: &Token,
132) -> Result<(Value, bool), JqlRunnerError> {
133    let result = match token {
134        Token::ArrayIndexSelector(indexes) => get_array_indexes(indexes, &acc),
135        Token::ArrayRangeSelector(range) => get_array_range(range, &mut acc),
136        Token::FlattenOperator => match acc {
137            Value::Array(_) => get_flattened_array(&acc),
138            Value::Object(_) => Ok(get_flattened_object(&acc)),
139            _ => Err(JqlRunnerError::FlattenError(acc)),
140        },
141        Token::KeyOperator => match acc {
142            Value::Array(_) => get_array_as_indexes(&acc),
143            Value::Object(_) => get_object_as_keys(&mut acc),
144            // Return the original value for Null, Bool, Number and String.
145            Value::Bool(bool) => Ok(json!(bool)),
146            Value::Number(number) => Ok(json!(number)),
147            Value::String(string) => Ok(json!(string)),
148            Value::Null => Ok(json!(null)),
149        },
150        Token::GroupSeparator => unreachable!(),
151        Token::KeySelector(key) => get_object_key(key, &acc),
152        Token::LensSelector(lenses) => get_array_lenses(lenses, &mut acc),
153        Token::MultiKeySelector(keys) => get_object_multi_key(keys, &mut acc),
154        Token::ObjectIndexSelector(indexes) => get_object_indexes(indexes, &mut acc),
155        Token::ObjectRangeSelector(range) => get_object_range(range, &mut acc),
156        Token::PipeInOperator => {
157            if !acc.is_array() {
158                return Err(JqlRunnerError::PipeInError(acc));
159            }
160
161            piped = true;
162
163            Ok(acc)
164        }
165        Token::PipeOutOperator => {
166            if !piped {
167                return Err(JqlRunnerError::PipeOutError);
168            }
169
170            piped = false;
171
172            Ok(acc)
173        }
174        Token::TruncateOperator => match acc {
175            Value::Array(_) => Ok(json!([])),
176            Value::Object(_) => Ok(json!({})),
177            Value::Bool(_) | Value::Number(_) | Value::String(_) | Value::Null => Ok(acc),
178        },
179    };
180
181    result.map(|value| (value, piped))
182}
183
184#[cfg(test)]
185mod tests {
186    use jql_parser::{
187        errors::JqlParserError,
188        tokens::{
189            Token,
190            View,
191        },
192    };
193    use serde_json::json;
194
195    use super::raw;
196    use crate::errors::JqlRunnerError;
197
198    #[test]
199    fn check_runner_empty_input_error() {
200        assert_eq!(raw("", &json!("")), Err(JqlRunnerError::EmptyQueryError));
201    }
202
203    #[test]
204    fn check_runner_parsing_error() {
205        assert_eq!(
206            raw(r#""a"b"#, &json!({ "a": 1 })),
207            Err(JqlRunnerError::ParsingError(JqlParserError::ParsingError {
208                tokens: [Token::KeySelector("a")].stringify(),
209                unparsed: "b".to_string(),
210            }))
211        );
212    }
213
214    #[test]
215    fn check_runner_no_key_found_error() {
216        let parent = json!({ "a": 1 });
217
218        assert_eq!(
219            raw(r#""b""#, &parent),
220            Err(JqlRunnerError::KeyNotFoundError {
221                key: "b".to_string(),
222                parent
223            })
224        );
225    }
226
227    #[test]
228    fn check_runner_index_not_found_error() {
229        let parent = json!(["a"]);
230
231        assert_eq!(
232            raw("[1]", &parent),
233            Err(JqlRunnerError::IndexOutOfBoundsError { index: 1, parent })
234        );
235    }
236
237    #[test]
238    fn check_runner_success() {
239        assert_eq!(
240            raw(r#""a","b""#, &json!({ "a": 1, "b": 2 })),
241            Ok(json!([1, 2]))
242        );
243        assert_eq!(raw(r#""a""b""#, &json!({ "a": { "b": 2 } })), Ok(json!(2)));
244        assert_eq!(
245            raw("[4,2,0]", &json!(["a", "b", "c", "d", "e"])),
246            Ok(json!(["e", "c", "a"]))
247        );
248    }
249
250    #[test]
251    fn check_runner_pipes() {
252        let value = json!({ "a": [{ "b": { "c": 1 } }, { "b": { "c": 2 }}]});
253
254        assert_eq!(raw(r#""a"|>"b""c"<|[1]"#, &value), Ok(json!(2)));
255    }
256
257    #[test]
258    fn check_runner_truncate() {
259        assert_eq!(raw(r#""a"!"#, &json!({ "a": [1, 2, 3] })), Ok(json!([])));
260        assert_eq!(raw(r#""a"!"#, &json!({ "a": { "b": 1 } })), Ok(json!({})));
261        assert_eq!(raw(r#""a"!"#, &json!({ "a": true })), Ok(json!(true)));
262        assert_eq!(raw(r#""a"!"#, &json!({ "a": 1 })), Ok(json!(1)));
263        assert_eq!(raw(r#""a"!"#, &json!({ "a": "b" })), Ok(json!("b")));
264        assert_eq!(raw(r#""a"!"#, &json!({ "a": null })), Ok(json!(null)));
265        assert_eq!(raw("!", &json!({ "a": null })), Ok(json!({})));
266        assert_eq!(
267            raw(r#""a"!"b""#, &json!({ "a": [1, 2, 3] })),
268            Err(JqlRunnerError::ParsingError(JqlParserError::TruncateError(
269                [
270                    Token::KeySelector("a"),
271                    Token::TruncateOperator,
272                    Token::KeySelector("b")
273                ]
274                .stringify(),
275            )))
276        );
277    }
278
279    #[test]
280    fn check_runner_lens() {
281        let value = json!([
282            { "a": { "b": { "c": 1 }}},
283            { "a": { "b": { "c": 2 }}},
284        ]);
285
286        assert_eq!(
287            raw(r#"|={"a""b""c"=2}"#, &value),
288            Ok(json!([
289                { "a": { "b": { "c": 2 }}}
290            ]))
291        );
292    }
293
294    #[test]
295    fn check_runner_keys() {
296        let value = json!({ "a": { "b": { "c": { "d": 1 }}}});
297
298        assert_eq!(raw(r#""a""b""c"@"#, &value), Ok(json!(["d"])));
299    }
300}