jsonpath_rust/parser/
parser.rs

1#![allow(clippy::empty_docs)]
2
3use crate::parser::errors::JsonPathParserError;
4use crate::parser::model::FilterExpression::{And, Not, Or};
5use crate::parser::model::{
6    FilterExpression, FilterSign, Function, JsonPath, JsonPathIndex, Operand,
7};
8use crate::path::JsonLike;
9use pest::iterators::{Pair, Pairs};
10use pest::Parser;
11
12#[derive(Parser)]
13#[grammar = "parser/grammar/json_path.pest"]
14struct JsonPathParser;
15
16/// Parses a string into a [JsonPath].
17///
18/// # Errors
19///
20/// Returns a variant of [JsonPathParserError] if the parsing operation failed.
21pub fn parse_json_path<T>(jp_str: &str) -> Result<JsonPath<T>, JsonPathParserError>
22where
23    T: JsonLike,
24{
25    JsonPathParser::parse(Rule::path, jp_str)
26        .map_err(Box::new)?
27        .next()
28        .ok_or(JsonPathParserError::UnexpectedPestOutput)
29        .and_then(parse_internal)
30}
31
32/// Internal function takes care of the logic by parsing the operators and unrolling the string into the final result.
33///
34/// # Errors
35///
36/// Returns a variant of [JsonPathParserError] if the parsing operation failed
37fn parse_internal<T>(rule: Pair<'_, Rule>) -> Result<JsonPath<T>, JsonPathParserError>
38where
39    T: JsonLike,
40{
41    match rule.as_rule() {
42        Rule::path => rule
43            .into_inner()
44            .next()
45            .ok_or(JsonPathParserError::NoRulePath)
46            .and_then(parse_internal),
47        Rule::current => rule
48            .into_inner()
49            .next()
50            .map(parse_internal)
51            .unwrap_or(Ok(JsonPath::Empty))
52            .map(Box::new)
53            .map(JsonPath::Current),
54        Rule::chain => rule
55            .into_inner()
56            .map(parse_internal)
57            .collect::<Result<Vec<_>, _>>()
58            .map(JsonPath::Chain),
59        Rule::root => Ok(JsonPath::Root),
60        Rule::wildcard => Ok(JsonPath::Wildcard),
61        Rule::descent => parse_key(down(rule)?)?
62            .map(JsonPath::Descent)
63            .ok_or(JsonPathParserError::NoJsonPathDescent),
64        Rule::descent_w => Ok(JsonPath::DescentW),
65        Rule::function => Ok(JsonPath::Fn(Function::Length)),
66        Rule::field => parse_key(down(rule)?)?
67            .map(JsonPath::Field)
68            .ok_or(JsonPathParserError::NoJsonPathField),
69        Rule::index => parse_index(rule).map(JsonPath::Index),
70        rule => Err(JsonPathParserError::InvalidTopLevelRule(rule)),
71    }
72}
73
74/// parsing the rule 'key' with the structures either .key or .\['key'\]
75fn parse_key(rule: Pair<Rule>) -> Result<Option<String>, JsonPathParserError> {
76    let parsed_key = match rule.as_rule() {
77        Rule::key | Rule::key_unlim | Rule::string_qt => parse_key(down(rule)?),
78        Rule::key_lim | Rule::inner => Ok(Some(String::from(rule.as_str()))),
79        _ => Ok(None),
80    };
81    parsed_key
82}
83
84fn parse_slice<T>(pairs: Pairs<Rule>) -> Result<JsonPathIndex<T>, JsonPathParserError> {
85    let mut start = 0;
86    let mut end = 0;
87    let mut step = 1;
88    for in_pair in pairs {
89        match in_pair.as_rule() {
90            Rule::start_slice => start = in_pair.as_str().parse::<i32>().unwrap_or(start),
91            Rule::end_slice => end = in_pair.as_str().parse::<i32>().unwrap_or(end),
92            Rule::step_slice => step = down(in_pair)?.as_str().parse::<usize>().unwrap_or(step),
93            _ => (),
94        }
95    }
96    Ok(JsonPathIndex::Slice(start, end, step))
97}
98
99fn parse_unit_keys<T>(pairs: Pairs<Rule>) -> Result<JsonPathIndex<T>, JsonPathParserError> {
100    let mut keys = vec![];
101
102    for pair in pairs {
103        keys.push(String::from(down(pair)?.as_str()));
104    }
105    Ok(JsonPathIndex::UnionKeys(keys))
106}
107
108fn number_to_value<T>(number: &str) -> Result<T, JsonPathParserError>
109where
110    T: From<i64> + From<f64>,
111{
112    match number
113        .parse::<i64>()
114        .ok()
115        .map(T::from)
116        .or_else(|| number.parse::<f64>().ok().map(T::from))
117    {
118        Some(value) => Ok(value),
119        None => Err(JsonPathParserError::InvalidNumber(number.to_string())),
120    }
121}
122
123fn bool_to_value<T>(boolean: &str) -> T
124where
125    T: From<bool>,
126{
127    boolean
128        .parse::<bool>()
129        .map(T::from)
130        .expect("unreachable: according to .pest this is either `true` or `false`")
131}
132
133fn parse_unit_indexes<T>(pairs: Pairs<Rule>) -> Result<JsonPathIndex<T>, JsonPathParserError>
134where
135    T: From<i64> + From<f64>,
136{
137    let mut keys = vec![];
138
139    for pair in pairs {
140        keys.push(number_to_value(pair.as_str())?);
141    }
142    Ok(JsonPathIndex::UnionIndex(keys))
143}
144
145fn parse_chain_in_operand<T>(rule: Pair<'_, Rule>) -> Result<Operand<T>, JsonPathParserError>
146where
147    T: JsonLike,
148{
149    let parsed_chain = match parse_internal::<T>(rule)? {
150        JsonPath::Chain(elems) => {
151            if elems.len() == 1 {
152                match elems.first() {
153                    Some(JsonPath::Index(JsonPathIndex::UnionKeys(keys))) => {
154                        Operand::val(T::from(keys.clone()))
155                    }
156                    Some(JsonPath::Index(JsonPathIndex::UnionIndex(keys))) => {
157                        Operand::val(T::from(keys.clone()))
158                    }
159                    Some(JsonPath::Field(f)) => Operand::val(T::from(vec![f.to_string()])),
160                    _ => Operand::Dynamic(Box::new(JsonPath::Chain(elems))),
161                }
162            } else {
163                Operand::Dynamic(Box::new(JsonPath::Chain(elems)))
164            }
165        }
166        jp => Operand::Dynamic(Box::new(jp)),
167    };
168    Ok(parsed_chain)
169}
170
171fn parse_filter_index<T>(pair: Pair<'_, Rule>) -> Result<JsonPathIndex<T>, JsonPathParserError>
172where
173    T: JsonLike,
174{
175    Ok(JsonPathIndex::Filter(parse_logic_or(pair.into_inner())?))
176}
177
178fn parse_logic_or<T>(pairs: Pairs<'_, Rule>) -> Result<FilterExpression<T>, JsonPathParserError>
179where
180    T: JsonLike,
181{
182    let mut expr: Option<FilterExpression<T>> = None;
183    // only possible for the loop not to produce any value (except Errors)
184    if pairs.len() == 0 {
185        return Err(JsonPathParserError::UnexpectedNoneLogicError(
186            pairs.get_input().to_string(),
187            pairs.as_str().to_string(),
188        ));
189    }
190    for pair in pairs.into_iter() {
191        let next_expr = parse_logic_and(pair.into_inner())?;
192        match expr {
193            None => expr = Some(next_expr),
194            Some(e) => expr = Some(Or(Box::new(e), Box::new(next_expr))),
195        }
196    }
197    Ok(expr.expect("unreachable: above len() == 0 check should have catched this"))
198}
199
200fn parse_logic_and<T>(pairs: Pairs<'_, Rule>) -> Result<FilterExpression<T>, JsonPathParserError>
201where
202    T: JsonLike,
203{
204    let mut expr: Option<FilterExpression<T>> = None;
205    // only possible for the loop not to produce any value (except Errors)
206    if pairs.len() == 0 {
207        return Err(JsonPathParserError::UnexpectedNoneLogicError(
208            pairs.get_input().to_string(),
209            pairs.as_str().to_string(),
210        ));
211    }
212    for pair in pairs {
213        let next_expr = parse_logic_not(pair.into_inner())?;
214        match expr {
215            None => expr = Some(next_expr),
216            Some(e) => expr = Some(And(Box::new(e), Box::new(next_expr))),
217        }
218    }
219    Ok(expr.expect("unreachable: above len() == 0 check should have catched this"))
220}
221
222fn parse_logic_not<T>(
223    mut pairs: Pairs<'_, Rule>,
224) -> Result<FilterExpression<T>, JsonPathParserError>
225where
226    T: JsonLike,
227{
228    if let Some(rule) = pairs.peek().map(|x| x.as_rule()) {
229        match rule {
230            Rule::not => {
231                pairs.next().expect("unreachable in arithmetic: should have a value as pairs.peek() was Some(_)");
232                parse_logic_not(pairs)
233                    .map(|expr|Not(Box::new(expr)))
234            },
235            Rule::logic_atom => parse_logic_atom(pairs.next().expect("unreachable in arithmetic: should have a value as pairs.peek() was Some(_)").into_inner()),
236            rule => Err(JsonPathParserError::UnexpectedRuleLogicError(rule, pairs.get_input().to_string(), pairs.as_str().to_string())),
237        }
238    } else {
239        Err(JsonPathParserError::UnexpectedNoneLogicError(
240            pairs.get_input().to_string(),
241            pairs.as_str().to_string(),
242        ))
243    }
244}
245
246fn parse_logic_atom<T>(
247    mut pairs: Pairs<'_, Rule>,
248) -> Result<FilterExpression<T>, JsonPathParserError>
249where
250    T: JsonLike,
251{
252    if let Some(rule) = pairs.peek().map(|x| x.as_rule()) {
253        match rule {
254            Rule::logic_or => parse_logic_or(pairs.next().expect("unreachable in arithmetic: should have a value as pairs.peek() was Some(_)").into_inner()),
255            Rule::atom => {
256                let left: Operand<T> = parse_atom(pairs.next().unwrap())?;
257                if pairs.peek().is_none() {
258                    Ok(FilterExpression::exists(left))
259                } else {
260                    let sign: FilterSign = FilterSign::new(pairs.next().expect("unreachable in arithmetic: should have a value as pairs.peek() was Some(_)").as_str());
261                    let right: Operand<T> =
262                        parse_atom(pairs.next().expect("unreachable in arithemetic: should have a right side operand"))?;
263                    Ok(FilterExpression::Atom(left, sign, right))
264                }
265            }
266            rule => Err(JsonPathParserError::UnexpectedRuleLogicError(rule, pairs.get_input().to_string(), pairs.as_str().to_string())),
267        }
268    } else {
269        Err(JsonPathParserError::UnexpectedNoneLogicError(
270            pairs.get_input().to_string(),
271            pairs.as_str().to_string(),
272        ))
273    }
274}
275
276fn parse_atom<T>(rule: Pair<'_, Rule>) -> Result<Operand<T>, JsonPathParserError>
277where
278    T: JsonLike,
279{
280    let atom = down(rule.clone())?;
281    let parsed_atom = match atom.as_rule() {
282        Rule::number => Operand::Static(number_to_value(rule.as_str())?),
283        Rule::string_qt => Operand::Static(T::from(down(atom)?.as_str())),
284        Rule::chain => parse_chain_in_operand(down(rule)?)?,
285        Rule::boolean => Operand::Static(bool_to_value(rule.as_str())),
286        _ => Operand::Static(T::null()),
287    };
288    Ok(parsed_atom)
289}
290
291fn parse_index<T>(rule: Pair<'_, Rule>) -> Result<JsonPathIndex<T>, JsonPathParserError>
292where
293    T: JsonLike,
294{
295    let next = down(rule)?;
296    let parsed_index = match next.as_rule() {
297        Rule::unsigned => JsonPathIndex::Single(number_to_value(next.as_str())?),
298        Rule::slice => parse_slice(next.into_inner())?,
299        Rule::unit_indexes => parse_unit_indexes(next.into_inner())?,
300        Rule::unit_keys => parse_unit_keys(next.into_inner())?,
301        Rule::filter => parse_filter_index(down(next)?)?,
302        _ => JsonPathIndex::Single(number_to_value(next.as_str())?),
303    };
304    Ok(parsed_index)
305}
306
307fn down(rule: Pair<Rule>) -> Result<Pair<Rule>, JsonPathParserError> {
308    let error_message = rule.to_string();
309    match rule.into_inner().next() {
310        Some(rule) => Ok(rule),
311        None => Err(JsonPathParserError::EmptyInner(error_message)),
312    }
313}
314
315#[cfg(test)]
316mod tests {
317    use super::*;
318    use crate::parser::macros::{chain, filter, idx, op};
319    use crate::path;
320    use serde_json::{json, Value};
321    use std::panic;
322
323    fn test_failed(input: &str) {
324        match parse_json_path::<Value>(input) {
325            Ok(elem) => panic!("should be false but got {:?}", elem),
326            Err(e) => println!("{}", e),
327        }
328    }
329
330    fn test<T>(input: &str, expected: Vec<JsonPath<T>>)
331    where
332        T: JsonLike,
333    {
334        match parse_json_path::<T>(input) {
335            Ok(JsonPath::Chain(elems)) => assert_eq!(elems, expected),
336            Ok(e) => panic!("unexpected value {:?}", e),
337            Err(e) => {
338                panic!("parsing error {}", e);
339            }
340        }
341    }
342
343    #[test]
344    fn path_test() {
345        test::<Value>("$.k.['k']['k']..k..['k'].*.[*][*][1][1,2]['k','k'][:][10:][:10][10:10:10][?(@)][?(@.abc >= 10)]",
346             vec![
347                 path!($),
348                 path!("k"),
349                 path!("k"),
350                 path!("k"),
351                 path!(.."k"),
352                 path!(.."k"),
353                 path!(*),
354                 path!(*),
355                 path!(*),
356                 path!(idx!(1)),
357                 path!(idx!(idx 1,2)),
358                 path!(idx!("k","k")),
359                 path!(idx!([; ;])),
360                 path!(idx!([10; ;])),
361                 path!(idx!([;10;])),
362                 path!(idx!([10;10;10])),
363                 path!(idx!(?filter!(op!(chain!(path!(@path!()))), "exists", op!(path!())))),
364                 path!(idx!(?filter!(op!(chain!(path!(@,path!("abc")))), ">=", op!(10)))),
365             ]);
366        test::<Value>(
367            "$..*[?(@.isbn)].title",
368            vec![
369                // Root, DescentW, Index(Filter(Atom(Dynamic(Chain([Current(Chain([Field("isbn")]))])), Exists, Dynamic(Empty)))), Field("title")
370                path!($),
371                path!(..*),
372                path!(idx!(?filter!(op!(chain!(path!(@,path!("isbn")))), "exists", op!(path!())))),
373                path!("title"),
374            ],
375        )
376    }
377
378    #[test]
379    fn descent_test() {
380        test::<Value>("..abc", vec![path!(.."abc")]);
381        test::<Value>("..['abc']", vec![path!(.."abc")]);
382        test_failed("...['abc']");
383        test_failed("...abc");
384    }
385
386    #[test]
387    fn field_test() {
388        test::<Value>(".abc", vec![path!("abc")]);
389        test::<Value>(".['abc']", vec![path!("abc")]);
390        test::<Value>("['abc']", vec![path!("abc")]);
391        test::<Value>(".['abc\\\"abc']", vec![path!("abc\\\"abc")]);
392        test_failed(".abc()abc");
393        test_failed("..[abc]");
394        test_failed(".'abc'");
395    }
396
397    #[test]
398    fn wildcard_test() {
399        test::<Value>(".*", vec![path!(*)]);
400        test::<Value>(".[*]", vec![path!(*)]);
401        test::<Value>(".abc.*", vec![path!("abc"), path!(*)]);
402        test::<Value>(".abc.[*]", vec![path!("abc"), path!(*)]);
403        test::<Value>(".abc[*]", vec![path!("abc"), path!(*)]);
404        test::<Value>("..*", vec![path!(..*)]);
405        test_failed("abc*");
406    }
407
408    #[test]
409    fn index_single_test() {
410        test::<Value>("[1]", vec![path!(idx!(1))]);
411        test_failed("[-1]");
412        test_failed("[1a]");
413    }
414
415    #[test]
416    fn index_slice_test() {
417        test::<Value>("[1:1000:10]", vec![path!(idx!([1; 1000; 10]))]);
418        test::<Value>("[:1000:10]", vec![path!(idx!([0; 1000; 10]))]);
419        test::<Value>("[:1000]", vec![path!(idx!([;1000;]))]);
420        test::<Value>("[:]", vec![path!(idx!([;;]))]);
421        test::<Value>("[::10]", vec![path!(idx!([;;10]))]);
422        test_failed("[::-1]");
423        test_failed("[:::0]");
424    }
425
426    #[test]
427    fn index_union_test() {
428        test::<Value>("[1,2,3]", vec![path!(idx!(idx 1,2,3))]);
429        test::<Value>("['abc','bcd']", vec![path!(idx!("abc", "bcd"))]);
430        test_failed("[]");
431        test::<Value>("[-1,-2]", vec![path!(idx!(idx - 1, -2))]);
432        test_failed("[abc,bcd]");
433        test::<Value>("[\"abc\",\"bcd\"]", vec![path!(idx!("abc", "bcd"))]);
434    }
435
436    #[test]
437    fn array_start_test() {
438        test::<Value>(
439            "$.[?(@.verb== \"TEST\")]",
440            vec![
441                path!($),
442                path!(idx!(?filter!(op!(chain!(path!(@,path!("verb")))),"==",op!("TEST")))),
443            ],
444        );
445    }
446
447    #[test]
448    fn logical_filter_test() {
449        test::<Value>(
450            "$.[?(@.verb == 'T' || @.size > 0 && @.size < 10)]",
451            vec![
452                path!($),
453                path!(idx!(?
454                filter!(
455                    filter!(op!(chain!(path!(@,path!("verb")))), "==", op!("T")),
456                    ||,
457                    filter!(
458                        filter!(op!(chain!(path!(@,path!("size")))), ">", op!(0)),
459                        &&,
460                        filter!(op!(chain!(path!(@,path!("size")))), "<", op!(10))
461                    )
462                ))),
463            ],
464        );
465        test::<Value>(
466            "$.[?((@.verb == 'T' || @.size > 0) && @.size < 10)]",
467            vec![
468                path!($),
469                path!(idx!(?
470                filter!(
471                    filter!(
472                       filter!(op!(chain!(path!(@,path!("verb")))), "==", op!("T")),
473                        ||,
474                        filter!(op!(chain!(path!(@,path!("size")))), ">", op!(0))
475                    ),
476                    &&,
477                    filter!(op!(chain!(path!(@,path!("size")))), "<", op!(10))
478                ))),
479            ],
480        );
481        test::<Value>(
482            "$.[?(@.verb == 'T' || @.size > 0 && @.size < 10 && @.elem == 0)]",
483            vec![
484                path!($),
485                path!(idx!(?filter!(
486                    filter!(op!(chain!(path!(@,path!("verb")))), "==", op!("T")),
487                    ||,
488                    filter!(
489                        filter!(
490                            filter!(op!(chain!(path!(@,path!("size")))), ">", op!(0)),
491                            &&,
492                            filter!(op!(chain!(path!(@,path!("size")))), "<", op!(10))
493                        ),
494                        &&,
495                        filter!(op!(chain!(path!(@,path!("elem")))), "==", op!(0))
496                    )
497
498                ))),
499            ],
500        );
501    }
502
503    #[test]
504    fn index_filter_test() {
505        test::<Value>(
506            "[?('abc' == 'abc')]",
507            vec![path!(idx!(?filter!(op!("abc"),"==",op!("abc") )))],
508        );
509        test::<Value>(
510            "[?('abc' == 1)]",
511            vec![path!(idx!(?filter!( op!("abc"),"==",op!(1))))],
512        );
513        test::<Value>(
514            "[?('abc' == true)]",
515            vec![path!(idx!(?filter!( op!("abc"),"==",op!(true))))],
516        );
517        test::<Value>(
518            "[?('abc' == null)]",
519            vec![path!(
520                idx!(?filter!( op!("abc"),"==",Operand::Static(Value::Null)))
521            )],
522        );
523
524        test::<Value>(
525            "[?(@.abc in ['abc','bcd'])]",
526            vec![path!(
527                idx!(?filter!(op!(chain!(path!(@,path!("abc")))),"in",Operand::val(json!(["abc","bcd"]))))
528            )],
529        );
530
531        test::<Value>(
532            "[?(@.abc.[*] in ['abc','bcd'])]",
533            vec![path!(idx!(?filter!(
534               op!(chain!(path!(@,path!("abc"), path!(*)))),
535                "in",
536                op!(s json!(["abc","bcd"]))
537            )))],
538        );
539        test::<Value>(
540            "[?(@.[*]..next in ['abc','bcd'])]",
541            vec![path!(idx!(?filter!(
542                op!(chain!(path!(@,path!(*), path!(.."next")))),
543                "in",
544                op!(s json!(["abc","bcd"]))
545            )))],
546        );
547
548        test::<Value>(
549            "[?(@[1] in ['abc','bcd'])]",
550            vec![path!(idx!(?filter!(
551                op!(chain!(path!(@,path!(idx!(1))))),
552                "in",
553                op!(s json!(["abc","bcd"]))
554            )))],
555        );
556        test::<Value>(
557            "[?(@ == 'abc')]",
558            vec![path!(idx!(?filter!(
559                op!(chain!(path!(@path!()))),"==",op!("abc")
560            )))],
561        );
562        test::<Value>(
563            "[?(@ subsetOf ['abc'])]",
564            vec![path!(idx!(?filter!(
565                op!(chain!(path!(@path!()))),"subsetOf",op!(s json!(["abc"]))
566            )))],
567        );
568        test::<Value>(
569            "[?(@[1] subsetOf ['abc','abc'])]",
570            vec![path!(idx!(?filter!(
571                op!(chain!(path!(@,path!(idx!(1))))),
572                "subsetOf",
573                op!(s json!(["abc","abc"]))
574            )))],
575        );
576        test::<Value>(
577            "[?(@ subsetOf [1,2,3])]",
578            vec![path!(idx!(?filter!(
579                op!(chain!(path!(@path!()))),"subsetOf",op!(s json!([1,2,3]))
580            )))],
581        );
582
583        test_failed("[?(@[1] subsetof ['abc','abc'])]");
584        test_failed("[?(@ >< ['abc','abc'])]");
585        test_failed("[?(@ in {\"abc\":1})]");
586    }
587
588    #[test]
589    fn fn_size_test() {
590        test::<Value>(
591            "$.k.length()",
592            vec![path!($), path!("k"), JsonPath::Fn(Function::Length)],
593        );
594
595        test::<Value>(
596            "$.k.length.field",
597            vec![path!($), path!("k"), path!("length"), path!("field")],
598        )
599    }
600
601    #[test]
602    fn parser_error_test_invalid_rule() {
603        let result = parse_json_path::<Value>("notapath");
604
605        assert!(result.is_err());
606        assert!(result
607            .err()
608            .unwrap()
609            .to_string()
610            .starts_with("Failed to parse rule"));
611    }
612
613    #[test]
614    fn parser_error_test_empty_rule() {
615        let result = parse_json_path::<Value>("");
616
617        assert!(result.is_err());
618        assert!(result
619            .err()
620            .unwrap()
621            .to_string()
622            .starts_with("Failed to parse rule"));
623    }
624}