Skip to main content

jsonpath_rust/query/
atom.rs

1use crate::parser::model::FilterAtom;
2use crate::query::queryable::Queryable;
3use crate::query::state::{Data, State};
4use crate::query::Query;
5
6impl Query for FilterAtom {
7    fn process<'a, T: Queryable>(&self, state: State<'a, T>) -> State<'a, T> {
8        match self {
9            FilterAtom::Filter { expr, not } => {
10                let bool_res = expr.process(state);
11                if *not {
12                    invert_bool(bool_res)
13                } else {
14                    bool_res
15                }
16            }
17            FilterAtom::Test { expr, not } => {
18                let new_state = |b| State::bool(b, state.root);
19                let res = expr.process(state.clone());
20                if expr.is_res_bool() {
21                    if *not {
22                        invert_bool(res)
23                    } else {
24                        res
25                    }
26                } else {
27                    // RFC 9535 2.3.5.2: a test expression is true when the
28                    // nodelist it produces is non-empty. The value of the nodes
29                    // does not take part in the decision.
30                    let struct_presented = match res.data {
31                        Data::Ref(_) => true,
32                        Data::Refs(elems) => !elems.is_empty(),
33                        _ => false,
34                    };
35
36                    if struct_presented {
37                        new_state(!*not)
38                    } else {
39                        new_state(*not)
40                    }
41                }
42            }
43            FilterAtom::Comparison(cmp) => cmp.process(state),
44        }
45    }
46}
47
48fn invert_bool<T: Queryable>(state: State<T>) -> State<T> {
49    let root = state.root;
50    State::bool(
51        !state.ok_val().and_then(|v| v.as_bool()).unwrap_or_default(),
52        root,
53    )
54}
55
56#[cfg(test)]
57mod tests {
58    use crate::parser::model::Comparable;
59    use crate::parser::model::Literal;
60    use crate::parser::model::SingularQuery;
61    use crate::parser::model::SingularQuerySegment;
62    use crate::parser::model::{Comparison, FilterAtom};
63    use crate::q_segment;
64    use crate::query::queryable::Queryable;
65    use crate::query::state::State;
66    use crate::query::Query;
67    use crate::{atom, comparable, lit};
68    use crate::{cmp, singular_query};
69    use crate::{filter_, q_segments};
70    use serde_json::json;
71
72    #[test]
73    fn test_comparison() {
74        let json = json!({"i": 1});
75        let atom = atom!(comparable!(lit!(i 1)), ">=", comparable!(lit!(i 1)));
76        let state = State::root(&json);
77        let res = atom.process(state);
78        assert_eq!(res.ok_val().and_then(|v| v.as_bool()), Some(true));
79    }
80
81    #[test]
82    fn test_not_filter_atom() {
83        let json = json!({"a": 1 , "b": 2});
84        let state = State::root(&json);
85
86        let f1 = filter_!(atom!(
87            comparable!(> SingularQuery::Current(vec![])),
88            ">",
89            comparable!(lit!(i 2))
90        ));
91        let f2 = filter_!(atom!(
92            comparable!(> singular_query!(b)),
93            "!=",
94            comparable!(> singular_query!(a))
95        ));
96
97        let atom_or = atom!(!filter_!(or f1.clone(), f2.clone()));
98        let atom_and = atom!(!filter_!(and f1, f2));
99
100        assert_eq!(
101            atom_or
102                .process(state.clone())
103                .ok_val()
104                .and_then(|v| v.as_bool()),
105            Some(true)
106        );
107        assert_eq!(
108            atom_and.process(state).ok_val().and_then(|v| v.as_bool()),
109            Some(true)
110        );
111    }
112}