jsonpath_rust/query/
comparison.rs

1use crate::parser::model::{Comparable, Comparison, Literal, SingularQuery, SingularQuerySegment};
2use crate::query::queryable::Queryable;
3use crate::query::state::{Data, Pointer, State};
4use crate::query::Query;
5
6impl Query for Comparison {
7    fn process<'a, T: Queryable>(&self, state: State<'a, T>) -> State<'a, T> {
8        let root = state.root;
9        let (lhs, rhs) = self.vals();
10        let lhs = lhs.process(state.clone());
11        let rhs = rhs.process(state);
12        match self {
13            Comparison::Eq(..) => State::bool(eq(lhs, rhs), root),
14            Comparison::Ne(..) => State::bool(!eq(lhs, rhs), root),
15            Comparison::Gt(..) => State::bool(lt(rhs, lhs), root),
16            Comparison::Gte(..) => State::bool(lt(rhs.clone(), lhs.clone()) || eq(lhs, rhs), root),
17            Comparison::Lt(..) => State::bool(lt(lhs, rhs), root),
18            Comparison::Lte(..) => State::bool(lt(lhs.clone(), rhs.clone()) || eq(lhs, rhs), root),
19        }
20    }
21}
22
23fn lt<'a, T: Queryable>(lhs: State<'a, T>, rhs: State<'a, T>) -> bool {
24    let cmp = |lhs: &T, rhs: &T| {
25        let lhs_f64 = lhs.as_f64().or_else(|| lhs.as_i64().map(|v| v as f64));
26        let rhs_f64 = rhs.as_f64().or_else(|| rhs.as_i64().map(|v| v as f64));
27        if let (Some(lhs_num), Some(rhs_num)) = (lhs_f64, rhs_f64) {
28            lhs_num < rhs_num
29        } else if let (Some(lhs), Some(rhs)) = (lhs.as_str(), rhs.as_str()) {
30            lhs < rhs
31        } else {
32            false
33        }
34    };
35
36    match (lhs.data, rhs.data) {
37        (Data::Value(lhs), Data::Value(rhs)) => cmp(&lhs, &rhs),
38        (Data::Value(v), Data::Ref(p)) => cmp(&v, p.inner),
39        (Data::Ref(p), Data::Value(v)) => cmp(p.inner, &v),
40        (Data::Ref(lhs), Data::Ref(rhs)) => cmp(lhs.inner, rhs.inner),
41        _ => false,
42    }
43}
44
45fn eq<'a, T: Queryable>(lhs_state: State<'a, T>, rhs_state: State<'a, T>) -> bool {
46    match (lhs_state.data, rhs_state.data) {
47        (Data::Value(lhs), Data::Value(rhs)) => eq_json(&lhs, &rhs),
48        (Data::Value(v), Data::Ref(p)) => eq_json(&v, p.inner),
49        (Data::Ref(p), Data::Value(v)) => eq_json(&v, p.inner),
50        (Data::Ref(lhs), Data::Ref(rhs)) => eq_json(lhs.inner, rhs.inner),
51        (Data::Refs(lhs), Data::Refs(rhs)) => lhs == rhs,
52        (Data::Ref(r), Data::Refs(rhs)) => eq_ref_to_array(r, &rhs),
53        (Data::Nothing, Data::Nothing) => true,
54        _ => false,
55    }
56}
57/// Compare two JSON values for equality.
58/// For numbers, it should implement interoperability for integer and float
59fn eq_json<T: Queryable>(lhs: &T, rhs: &T) -> bool {
60    let lhs_f64 = lhs.as_f64().or_else(|| lhs.as_i64().map(|v| v as f64));
61    let rhs_f64 = rhs.as_f64().or_else(|| rhs.as_i64().map(|v| v as f64));
62
63    if let (Some(lhs_num), Some(rhs_num)) = (lhs_f64, rhs_f64) {
64        (lhs_num - rhs_num).abs() < f64::EPSILON
65    } else {
66        lhs == rhs
67    }
68}
69fn eq_ref_to_array<T: Queryable>(r: Pointer<T>, rhs: &Vec<Pointer<T>>) -> bool {
70    r.inner.as_array().map_or(false, |array| {
71        eq_arrays(array, &rhs.iter().map(|p| p.inner).collect::<Vec<_>>())
72    })
73}
74
75fn eq_arrays<T: Queryable>(lhs: &Vec<T>, rhs: &Vec<&T>) -> bool {
76    lhs.len() == rhs.len() && lhs.iter().zip(rhs.iter()).all(|(a, b)| eq_json(a, *b))
77}
78
79#[cfg(test)]
80mod tests {
81    use crate::parser::model::{
82        Comparable, Comparison, Literal, SingularQuery, SingularQuerySegment,
83    };
84    use crate::query::state::{Data, Pointer, State};
85    use crate::query::Query;
86    use crate::singular_query;
87    use crate::{cmp, comparable, lit, q_segment, q_segments};
88    use serde_json::json;
89    #[test]
90    fn eq_comp_val() {
91        let data = json!({"key": "value"});
92        let state = State::root(&data);
93
94        let comparison = Comparison::Eq(comparable!(lit!(s "key")), comparable!(lit!(s "key")));
95        let result = comparison.process(state);
96        assert_eq!(result.ok_val(), Some(json!(true)));
97    }
98
99    #[test]
100    fn eq_comp_ref() {
101        let data = json!({"key": "value"});
102        let state = State::root(&data);
103
104        let comparison = Comparison::Eq(
105            comparable!(lit!(s "value")),
106            comparable!(> singular_query!(@ key)),
107        );
108
109        let result = comparison.process(state);
110        assert_eq!(result.ok_val(), Some(json!(true)));
111    }
112
113    #[test]
114    fn eq_comp_queries() {
115        let data = json!({"key": "value", "key2": "value"});
116        let state = State::root(&data);
117
118        let comparison = Comparison::Eq(
119            comparable!(> singular_query!(@ key)),
120            comparable!(> singular_query!(key2)),
121        );
122        let result = comparison.process(state);
123        assert_eq!(result.ok_val(), Some(json!(true)));
124    }
125
126    #[test]
127    fn neq_comp_val() {
128        let data = json!({"key": "value"});
129        let state = State::root(&data);
130
131        let comparison = Comparison::Ne(comparable!(lit!(s "key")), comparable!(lit!(s "key")));
132        let result = comparison.process(state);
133        assert_eq!(result.ok_val(), Some(json!(false)));
134    }
135
136    #[test]
137    fn less_than() {
138        let data = json!({"key": 3});
139        let state = State::root(&data);
140
141        let comparison = Comparison::Lt(
142            comparable!(lit!(i 2)),
143            comparable!(> singular_query!(@ key)),
144        );
145        let result = comparison.process(state);
146        assert_eq!(result.ok_val(), Some(json!(true)));
147    }
148
149    #[test]
150    fn less_than_false() {
151        let data = json!({"key": 1});
152        let state = State::root(&data);
153
154        let comparison = Comparison::Lt(
155            comparable!(lit!(i 2)),
156            comparable!(> singular_query!(@ key)),
157        );
158        let result = comparison.process(state);
159        assert_eq!(result.ok_val(), Some(json!(false)));
160    }
161
162    #[test]
163    fn less_true() {
164        let data = json!({"key": 1});
165        let state = State::root(&data);
166
167        let comparison = Comparison::Lt(
168            comparable!(> singular_query!(@ key)),
169            comparable!(lit!(i 2)),
170        );
171        let result = comparison.process(state);
172        assert_eq!(result.ok_val(), Some(json!(true)));
173    }
174}