1use std::cmp::Ordering;
2
3use crate::expr::part::{Next, Part};
4use crate::val::Value;
5
6impl Value {
7 pub fn compare(
8 &self,
9 other: &Self,
10 path: &[Part],
11 collate: bool,
12 numeric: bool,
13 ) -> Option<Ordering> {
14 let Some(p) = path.first() else {
15 return match (collate, numeric) {
16 (true, true) => self.natural_lexical_cmp(other),
17 (true, false) => self.lexical_cmp(other),
18 (false, true) => self.natural_cmp(other),
19 _ => self.partial_cmp(other),
20 };
21 };
22
23 match (self, other) {
24 (Value::Object(a), Value::Object(b)) => match p {
26 Part::Field(f) => compare_optional(a.get(f), b.get(f), path, collate, numeric),
27 _ => None,
28 },
29 (Value::Array(a), Value::Array(b)) => match p {
31 Part::All => {
32 for (a, b) in a.iter().zip(b.iter()) {
33 match a.compare(b, path.next(), collate, numeric) {
34 None | Some(Ordering::Equal) => continue,
35 o => return o,
36 }
37 }
38 Some(a.len().cmp(&b.len()))
39 }
40 Part::First => compare_optional(a.first(), b.first(), path, collate, numeric),
41 Part::Last => compare_optional(a.last(), b.last(), path, collate, numeric),
42 x => {
45 if let Some(idx) = x.as_old_index() {
46 compare_optional(a.get(idx), b.get(idx), path, collate, numeric)
47 } else {
48 for (a, b) in a.iter().zip(b.iter()) {
49 match a.compare(b, path, collate, numeric) {
50 None | Some(Ordering::Equal) => continue,
51 o => return o,
52 }
53 }
54 Some(a.len().cmp(&b.len()))
55 }
56 }
57 },
58 (Value::Set(a), Value::Set(b)) => match p {
59 Part::All => {
60 for (a, b) in a.iter().zip(b.iter()) {
61 match a.compare(b, path.next(), collate, numeric) {
62 None | Some(Ordering::Equal) => continue,
63 o => return o,
64 }
65 }
66
67 Some(a.len().cmp(&b.len()))
68 }
69 Part::First => compare_optional(a.first(), b.first(), path, collate, numeric),
70 Part::Last => compare_optional(a.last(), b.last(), path, collate, numeric),
71 x => {
72 if let Some(idx) = x.as_old_index() {
73 compare_optional(a.nth(idx), b.nth(idx), path, collate, numeric)
74 } else {
75 for (a, b) in a.iter().zip(b.iter()) {
76 match a.compare(b, path, collate, numeric) {
77 None | Some(Ordering::Equal) => continue,
78 o => return o,
79 }
80 }
81 Some(a.len().cmp(&b.len()))
82 }
83 }
84 },
85 (a, b) => match p {
87 Part::Field(f) => match (a, b) {
88 (Value::Object(a), _) => match a.get(f) {
90 Some(a) => a.compare(&Value::None, path.next(), collate, numeric),
91 None => Some(Ordering::Equal),
92 },
93 (_, Value::Object(b)) => match b.get(f) {
94 Some(b) => Value::None.compare(b, path.next(), collate, numeric),
95 None => Some(Ordering::Equal),
96 },
97 _ => Some(Ordering::Equal),
99 },
100 _ => a.compare(b, path.next(), collate, numeric),
102 },
103 }
104 }
105}
106
107#[inline]
108fn compare_optional(
109 a: Option<&Value>,
110 b: Option<&Value>,
111 path: &[Part],
112 collate: bool,
113 numeric: bool,
114) -> Option<Ordering> {
115 match (a, b) {
116 (Some(a), Some(b)) => a.compare(b, path.next(), collate, numeric),
117 (Some(_), None) => Some(Ordering::Greater),
118 (None, Some(_)) => Some(Ordering::Less),
119 (None, None) => Some(Ordering::Equal),
120 }
121}
122
123#[cfg(test)]
124mod tests {
125
126 use rstest::rstest;
127
128 use super::*;
129 use crate::expr::idiom::Idiom;
130 use crate::syn;
131
132 macro_rules! parse_val {
133 ($input:expr) => {
134 crate::val::convert_public_value_to_internal(syn::value($input).unwrap())
135 };
136 }
137
138 #[rstest]
139 #[case::none_eq_none(Value::None, Value::None, Idiom::default(), Some(Ordering::Equal))]
140 #[case::none_eq_none(parse_val!("{ test: { other: null, something: 456 } }"), parse_val!("{ test: { other: null, something: 123 } }"), syn::idiom("test.something").unwrap().into(), Some(Ordering::Greater))]
141 fn test_compare(
142 #[case] a: Value,
143 #[case] b: Value,
144 #[case] path: Idiom,
145 #[case] expected: Option<Ordering>,
146 ) {
147 let res = a.compare(&b, &path, false, false);
148 assert_eq!(res, expected);
149 }
150
151 #[test]
152 fn compare_none() {
153 let idi: Idiom = Default::default();
154 let one = parse_val!("{ test: { other: null, something: 456 } }");
155 let two = parse_val!("{ test: { other: null, something: 123 } }");
156 let res = one.compare(&two, &idi, false, false);
157 assert_eq!(res, Some(Ordering::Greater));
158 }
159
160 #[test]
161 fn compare_basic() {
162 let idi: Idiom = syn::idiom("test.something").unwrap().into();
163 let one = parse_val!("{ test: { other: null, something: 456 } }");
164 let two = parse_val!("{ test: { other: null, something: 123 } }");
165 let res = one.compare(&two, &idi, false, false);
166 assert_eq!(res, Some(Ordering::Greater));
167 }
168
169 #[test]
170 fn compare_basic_missing_left() {
171 let idi: Idiom = syn::idiom("test.something").unwrap().into();
172 let one = parse_val!("{ test: { other: null } }");
173 let two = parse_val!("{ test: { other: null, something: 123 } }");
174 let res = one.compare(&two, &idi, false, false);
175 assert_eq!(res, Some(Ordering::Less));
176 }
177
178 #[test]
179 fn compare_basic_missing_right() {
180 let idi: Idiom = syn::idiom("test.something").unwrap().into();
181 let one = parse_val!("{ test: { other: null, something: 456 } }");
182 let two = parse_val!("{ test: { other: null } }");
183 let res = one.compare(&two, &idi, false, false);
184 assert_eq!(res, Some(Ordering::Greater));
185 }
186
187 #[test]
188 fn compare_array() {
189 let idi: Idiom = syn::idiom("test.something.*").unwrap().into();
190 let one = parse_val!("{ test: { other: null, something: [4, 5, 6] } }");
191 let two = parse_val!("{ test: { other: null, something: [1, 2, 3] } }");
192 let res = one.compare(&two, &idi, false, false);
193 assert_eq!(res, Some(Ordering::Greater));
194 }
195
196 #[test]
197 fn compare_array_longer_left() {
198 let idi: Idiom = syn::idiom("test.something.*").unwrap().into();
199 let one = parse_val!("{ test: { other: null, something: [1, 2, 3, 4, 5, 6] } }");
200 let two = parse_val!("{ test: { other: null, something: [1, 2, 3] } }");
201 let res = one.compare(&two, &idi, false, false);
202 assert_eq!(res, Some(Ordering::Greater));
203 }
204
205 #[test]
206 fn compare_array_longer_right() {
207 let idi: Idiom = syn::idiom("test.something.*").unwrap().into();
208 let one = parse_val!("{ test: { other: null, something: [1, 2, 3] } }");
209 let two = parse_val!("{ test: { other: null, something: [1, 2, 3, 4, 5, 6] } }");
210 let res = one.compare(&two, &idi, false, false);
211 assert_eq!(res, Some(Ordering::Less));
212 }
213
214 #[test]
215 fn compare_array_missing_left() {
216 let idi: Idiom = syn::idiom("test.something.*").unwrap().into();
217 let one = parse_val!("{ test: { other: null, something: null } }");
218 let two = parse_val!("{ test: { other: null, something: [1, 2, 3] } }");
219 let res = one.compare(&two, &idi, false, false);
220 assert_eq!(res, Some(Ordering::Less));
221 }
222
223 #[test]
224 fn compare_array_missing_right() {
225 let idi: Idiom = syn::idiom("test.something.*").unwrap().into();
226 let one = parse_val!("{ test: { other: null, something: [4, 5, 6] } }");
227 let two = parse_val!("{ test: { other: null, something: null } }");
228 let res = one.compare(&two, &idi, false, false);
229 assert_eq!(res, Some(Ordering::Greater));
230 }
231
232 #[test]
233 fn compare_array_missing_value_left() {
234 let idi: Idiom = syn::idiom("test.something.*").unwrap().into();
235 let one = parse_val!("{ test: { other: null, something: [1, null, 3] } }");
236 let two = parse_val!("{ test: { other: null, something: [1, 2, 3] } }");
237 let res = one.compare(&two, &idi, false, false);
238 assert_eq!(res, Some(Ordering::Less));
239 }
240
241 #[test]
242 fn compare_array_missing_value_right() {
243 let idi: Idiom = syn::idiom("test.something.*").unwrap().into();
244 let one = parse_val!("{ test: { other: null, something: [1, 2, 3] } }");
245 let two = parse_val!("{ test: { other: null, something: [1, null, 3] } }");
246 let res = one.compare(&two, &idi, false, false);
247 assert_eq!(res, Some(Ordering::Greater));
248 }
249
250 #[test]
251 fn compare_last() {
252 let idi: Idiom = syn::idiom("test[$]").unwrap().into();
253 let one = parse_val!("{ test: [1,5] }");
254 let two = parse_val!("{ test: [2,4] }");
255 let res = one.compare(&two, &idi, false, false);
256 assert_eq!(res, Some(Ordering::Greater))
257 }
258
259 #[test]
260 fn compare_field_on_record_id() {
261 let idi: Idiom = syn::idiom("city.name").unwrap().into();
263 let one = parse_val!("{ city: city:1 }");
264 let two = parse_val!("{ city: city:2 }");
265 let res = one.compare(&two, &idi, false, false);
266 assert_eq!(res, Some(Ordering::Equal));
267 }
268
269 #[test]
270 fn compare_field_on_record_id_different_order() {
271 let idi: Idiom = syn::idiom("city.name").unwrap().into();
273 let one = parse_val!("{ city: city:100 }");
274 let two = parse_val!("{ city: city:1 }");
275 let res = one.compare(&two, &idi, false, false);
276 assert_eq!(res, Some(Ordering::Equal));
277 }
278
279 #[test]
280 fn compare_field_object_vs_record_id() {
281 let idi: Idiom = syn::idiom("city.name").unwrap().into();
283 let one = parse_val!("{ city: { name: 'San Francisco' } }");
284 let two = parse_val!("{ city: city:1 }");
285 let res = one.compare(&two, &idi, false, false);
286 assert_eq!(res, Some(Ordering::Greater));
288 }
289
290 #[test]
291 fn compare_field_record_id_vs_object() {
292 let idi: Idiom = syn::idiom("city.name").unwrap().into();
294 let one = parse_val!("{ city: city:1 }");
295 let two = parse_val!("{ city: { name: 'San Francisco' } }");
296 let res = one.compare(&two, &idi, false, false);
297 assert_eq!(res, Some(Ordering::Less));
299 }
300
301 #[test]
302 fn compare_field_object_missing_vs_record_id() {
303 let idi: Idiom = syn::idiom("city.name").unwrap().into();
305 let one = parse_val!("{ city: { country: 'USA' } }");
306 let two = parse_val!("{ city: city:1 }");
307 let res = one.compare(&two, &idi, false, false);
308 assert_eq!(res, Some(Ordering::Equal));
310 }
311
312 #[test]
313 fn compare_field_on_string() {
314 let idi: Idiom = syn::idiom("value.nested").unwrap().into();
316 let one = parse_val!("{ value: 'hello' }");
317 let two = parse_val!("{ value: 'world' }");
318 let res = one.compare(&two, &idi, false, false);
319 assert_eq!(res, Some(Ordering::Equal));
320 }
321
322 #[test]
323 fn compare_field_on_number() {
324 let idi: Idiom = syn::idiom("value.nested").unwrap().into();
326 let one = parse_val!("{ value: 42 }");
327 let two = parse_val!("{ value: 100 }");
328 let res = one.compare(&two, &idi, false, false);
329 assert_eq!(res, Some(Ordering::Equal));
330 }
331
332 #[test]
333 fn compare_field_mixed_types() {
334 let idi: Idiom = syn::idiom("value.nested").unwrap().into();
336 let one = parse_val!("{ value: 'hello' }");
337 let two = parse_val!("{ value: 42 }");
338 let res = one.compare(&two, &idi, false, false);
339 assert_eq!(res, Some(Ordering::Equal));
340 }
341
342 #[test]
343 fn compare_nested_field_on_record_id() {
344 let idi: Idiom = syn::idiom("user.city.name").unwrap().into();
346 let one = parse_val!("{ user: user:1 }");
347 let two = parse_val!("{ user: user:2 }");
348 let res = one.compare(&two, &idi, false, false);
349 assert_eq!(res, Some(Ordering::Equal));
350 }
351}