1use serde_json::{Number, Value};
5
6pub fn is_truthy(value: &Value) -> bool {
8 match value {
9 Value::Array(arr) => !arr.is_empty(),
10 Value::Bool(b) => *b,
11 Value::Null => false,
12 Value::Number(num) => num.as_f64().unwrap() != 0f64,
13 Value::Object(_) => true,
14 Value::String(s) => s != "",
15 }
16}
17
18pub fn is_strict_equal(a: &Value, b: &Value) -> bool {
20 use Value::*;
21
22 match (a, b) {
23 (Array(_), Array(_)) => false,
24 (Bool(a), Bool(b)) => a == b,
25 (Null, Null) => true,
26 (Number(a), Number(b)) => equal_numbers(a, b),
27 (Object(_), Object(_)) => false,
28 (String(a), String(b)) => a == b,
29 _ => false,
30 }
31}
32
33#[allow(clippy::float_cmp)]
36pub fn is_abstract_equal(a: &Value, b: &Value) -> bool {
37 use Value::*;
38
39 match (a, b) {
40 (Array(_), Array(_))
42 | (Bool(_), Bool(_))
43 | (Null, Null)
44 | (Number(_), Number(_))
45 | (Object(_), Object(_))
46 | (String(_), String(_)) => is_strict_equal(a, b),
47 (Null, _) | (_, Null) => false,
49 (Number(a), String(_)) => coerce_to_f64(b)
51 .map(|b| a.as_f64().unwrap() == b)
52 .unwrap_or(false),
53 (String(_), Number(b)) => coerce_to_f64(a)
55 .map(|a| a == b.as_f64().unwrap())
56 .unwrap_or(false),
57 (Bool(_), _) => coerce_to_f64(a)
59 .map(|a| is_abstract_equal(&Value::Number(serde_json::Number::from_f64(a).unwrap()), b))
60 .unwrap_or(false),
61 (_, Bool(_)) => coerce_to_f64(b)
63 .map(|b| is_abstract_equal(a, &Value::Number(serde_json::Number::from_f64(b).unwrap())))
64 .unwrap_or(false),
65 (Object(_), _) | (_, Object(_)) => false,
70 (String(a), Array(b)) | (Array(b), String(a)) => a == &arr_to_primitive_str(b),
73 (Number(_), Array(b)) => is_abstract_equal(a, &Value::String(arr_to_primitive_str(b))),
74 (Array(a), Number(_)) => is_abstract_equal(&Value::String(arr_to_primitive_str(a)), b),
75 }
76}
77
78pub fn less_than(a: &Value, b: &Value) -> bool {
80 use Value::*;
81
82 match (a, b) {
83 (Null, Null) => false,
84 (Bool(false), Bool(true)) => true,
85 (Bool(_), Bool(_)) => false,
86 (Object(_), _) | (_, Object(_)) => false,
87 (String(a), String(b)) => a < b,
88 (Array(_), Array(_)) | (Array(_), String(_)) | (String(_), Array(_)) => {
92 coerce_to_str(a) < coerce_to_str(b)
93 }
94 (Null, _) | (_, Null) | (Number(_), _) | (_, Number(_)) | (Bool(_), _) | (_, Bool(_)) => {
97 match (coerce_to_f64(a), coerce_to_f64(b)) {
98 (Some(a), Some(b)) => a < b,
99 _ => false,
100 }
101 }
102 }
103}
104
105pub fn less_equal_than(a: &Value, b: &Value) -> bool {
106 less_than(a, b) || is_abstract_equal(a, b)
107}
108
109pub fn greater_than(a: &Value, b: &Value) -> bool {
110 !less_equal_than(a, b)
111}
112
113pub fn greater_equal_than(a: &Value, b: &Value) -> bool {
114 !less_than(a, b)
115}
116
117pub fn coerce_to_str(val: &Value) -> String {
119 match val {
120 Value::Array(arr) => arr_to_primitive_str(arr),
121 Value::Bool(b) => b.to_string(),
122 Value::Null => String::from("null"),
123 Value::Number(num) => num.to_string(),
124 Value::Object(_) => String::from("[object Object]"),
125 Value::String(s) => s.to_string(),
126 }
127}
128
129pub fn coerce_to_f64(val: &Value) -> Option<f64> {
131 match val {
132 Value::Array(arr) => match &arr[..] {
133 [] => Some(0f64),
134 [el] => match el {
136 Value::Array(_) | Value::Null | Value::Number(_) | Value::String(_) => {
137 coerce_to_f64(el)
138 }
139 _ => None,
140 },
141 _ => None,
142 },
143 Value::Bool(true) => Some(1f64),
144 Value::Bool(false) => Some(0f64),
145 Value::Null => Some(0f64),
146 Value::Number(num) => num.as_f64(),
147 Value::Object(_) => None,
148 Value::String(s) => {
149 let s = s.trim();
150 if s == "" {
151 Some(0f64)
152 } else {
153 s.parse::<f64>().ok()
154 }
155 }
156 }
157}
158
159pub fn parse_float(val: &Value) -> Option<f64> {
176 match val {
177 Value::Number(num) => Some(num.as_f64().unwrap()),
178 Value::String(s) => {
179 let s = s.trim();
180 let mut end = 0;
181 let mut has_decimal_point = false;
183 for ch in s.chars() {
184 match ch {
185 '+' | '-' | '0'..='9' | 'e' | 'E' => end += 1,
186 '.' => {
187 if has_decimal_point {
188 break;
189 } else {
190 end += 1;
191 has_decimal_point = true;
192 }
193 }
194 _ => break,
195 }
196 }
197
198 let parsed = &s[0..end];
199 parsed.parse::<f64>().ok()
200 }
201 _ => None,
202 }
203}
204
205#[allow(clippy::float_cmp)]
206fn equal_numbers(a: &Number, b: &Number) -> bool {
207 if a.is_u64() && b.is_u64() {
209 a.as_u64().unwrap() == b.as_u64().unwrap()
210 } else if a.is_i64() && b.is_i64() {
211 a.as_i64().unwrap() == b.as_i64().unwrap()
212 } else {
213 a.as_f64().unwrap() == b.as_f64().unwrap()
214 }
215}
216
217fn arr_to_primitive_str(arr: &[Value]) -> String {
221 arr.iter()
222 .map(|el| coerce_to_str(el))
223 .collect::<Vec<String>>()
224 .join(",")
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 use serde_json::json;
231
232 mod strict_equal {
233 use super::*;
234
235 macro_rules! test_strict_equal {
236 ($a:expr, $b:expr) => {
237 assert!(is_strict_equal(&json!($a), &json!($b)));
238 assert!(is_strict_equal(&json!($b), &json!($a)));
239 };
240 }
241
242 macro_rules! test_strict_not_equal {
243 ($a:expr, $b:expr) => {
244 assert!(!is_strict_equal(&json!($a), &json!($b)));
245 assert!(!is_strict_equal(&json!($b), &json!($a)));
246 };
247 }
248
249 #[test]
250 fn same_type_string() {
251 test_strict_equal!("", "");
252 test_strict_not_equal!(" ", "");
253 test_strict_equal!("a", "a");
254 test_strict_not_equal!("a", "b");
255 }
256
257 #[test]
258 fn same_type_number() {
259 test_strict_equal!(0, 0);
260 test_strict_equal!(0, 0.0);
261 test_strict_equal!(-0, 0);
262 test_strict_not_equal!(-1, 1);
263 test_strict_not_equal!(1.1, 1);
264 }
265
266 #[test]
267 fn same_type_bool() {
268 test_strict_equal!(true, true);
269 test_strict_equal!(false, false);
270 test_strict_not_equal!(false, true);
271 }
272
273 #[test]
274 fn same_type_object() {
275 assert!(!is_strict_equal(&json!({}), &json!({}),));
276 assert!(!is_strict_equal(
277 &json!({"foo": "bar"}),
278 &json!({"foo": "bar"}),
279 ));
280 assert!(!is_strict_equal(
281 &json!({"foo": "bar"}),
282 &json!({"foo": 1}),
283 ));
284 }
285
286 #[test]
287 fn same_type_array() {
288 assert!(!is_strict_equal(&json!([]), &json!([])));
289 assert!(!is_strict_equal(&json!([1]), &json!([1])));
290 assert!(!is_strict_equal(&json!([1]), &json!([2])));
291 }
292
293 #[test]
294 fn different_type() {
295 test_strict_not_equal!("", 0);
296 test_strict_not_equal!("1", 1);
297 test_strict_not_equal!("true", true);
298 test_strict_not_equal!(1, true);
299 assert!(!is_strict_equal(&json!(null), &json!(false)));
300 }
301 }
302
303 mod abstract_equal {
304 use super::*;
305
306 macro_rules! test_abstract_equal {
307 ($a:expr, $b:expr) => {
308 assert!(is_abstract_equal(&json!($a), &json!($b)));
309 assert!(is_abstract_equal(&json!($b), &json!($a)));
310 };
311 }
312 macro_rules! test_abstract_not_equal {
313 ($a:expr, $b:expr) => {
314 assert!(!is_abstract_equal(&json!($a), &json!($b)),);
315 assert!(!is_abstract_equal(&json!($b), &json!($a)),);
316 };
317 }
318 #[test]
319 fn loose_equal_same_type() {
320 test_abstract_equal!(Value::Null, Value::Null);
321 test_abstract_equal!(true, true);
322 test_abstract_equal!(false, false);
323 test_abstract_equal!("foo", "foo");
324 test_abstract_equal!(0, 0);
325 test_abstract_equal!(0, -0);
326 test_abstract_equal!(0, 0.0);
327 test_abstract_equal!(0.2, 0.2);
328 }
329 #[test]
330 fn loose_equal_diff_type() {
331 test_abstract_equal!([1, 2], "1,2");
332 }
333 #[test]
334 fn loose_not_equal() {
335 test_abstract_not_equal!(0, &Value::Null);
336 }
337 #[test]
338 fn number_boolean() {
339 test_abstract_equal!(-0, false);
340 test_abstract_equal!(0, false);
341 test_abstract_equal!(1, true);
342 test_abstract_equal!(1.0, true);
343 test_abstract_not_equal!(-1, true);
344 test_abstract_not_equal!(0.1 + 0.2, false);
345 }
346 #[test]
347 fn number_string() {
348 test_abstract_equal!("", 0);
349 test_abstract_equal!("0", 0);
350 test_abstract_equal!("-0", 0);
351 test_abstract_equal!("+0", 0);
352 test_abstract_equal!("0.0", 0);
353 test_abstract_equal!("+0.0", 0);
354 test_abstract_equal!("-0.0", 0);
355 test_abstract_equal!("17", 17);
356 test_abstract_equal!("-17", -17);
357 test_abstract_equal!(" 1 ", 1);
358 test_abstract_equal!(" 1.3 ", 1.3);
359 }
360 #[test]
361 fn array_bool() {
362 test_abstract_equal!([1], true);
363 test_abstract_not_equal!([true], true);
364 }
365 #[test]
366 fn string_bool() {
367 test_abstract_equal!("", false);
368 test_abstract_equal!(" ", false);
369 test_abstract_equal!("0", false);
370 test_abstract_equal!(" 0 ", false);
371 test_abstract_equal!("1", true);
372 test_abstract_equal!(" 1 ", true);
373 }
374 #[test]
375 fn number_array() {
376 test_abstract_equal!([1], 1);
377 test_abstract_equal!([1.2], 1.2);
378 }
379 }
380
381 mod test_less_than {
382 use super::*;
383
384 macro_rules! less_than {
385 ($a:expr, $b:expr, $result:expr) => {
386 assert_eq!(less_than(&json!($a), &json!($b)), $result);
387 };
388 }
389
390 #[test]
391 fn same_type() {
392 assert_eq!(less_than(&json!(1), &json!(2)), true);
394 assert_eq!(less_than(&json!(2), &json!(2)), false);
395 assert_eq!(less_than(&json!(3), &json!(2)), false);
396
397 assert_eq!(less_than(&json!("a"), &json!("b")), true);
399 assert_eq!(less_than(&json!("b"), &json!("b")), false);
400 assert_eq!(less_than(&json!("c"), &json!("b")), false);
401
402 assert_eq!(less_than(&json!(null), &json!(null)), false);
404
405 assert_eq!(less_than(&json!(false), &json!(true)), true);
407 assert_eq!(less_than(&json!(true), &json!(false)), false);
408 assert_eq!(less_than(&json!(true), &json!(true)), false);
409 assert_eq!(less_than(&json!(false), &json!(false)), false);
410 }
411
412 #[test]
413 fn number_string() {
414 assert_eq!(less_than(&json!(1), &json!("b")), false);
416 assert_eq!(less_than(&json!(1), &json!("1")), false);
417 assert_eq!(less_than(&json!(-1), &json!("")), true);
418 assert_eq!(less_than(&json!(1), &json!("12")), true);
419
420 assert_eq!(less_than(&json!("b"), &json!(1)), false);
422 assert_eq!(less_than(&json!("1"), &json!(1)), false);
423 assert_eq!(less_than(&json!(""), &json!(-1)), false);
424 assert_eq!(less_than(&json!("12"), &json!(1)), false);
425 }
426
427 #[test]
428 fn array_number() {
429 assert_eq!(less_than(&json!([1]), &json!(12)), true);
431 assert_eq!(less_than(&json!([2]), &json!(12)), true);
432 assert_eq!(less_than(&json!([[2]]), &json!(12)), true);
433 assert_eq!(less_than(&json!([[2], 3]), &json!(12)), false);
434
435 assert_eq!(less_than(&json!(1), &json!([12])), true);
437 assert_eq!(less_than(&json!(2), &json!([12])), true);
438 assert_eq!(less_than(&json!(2), &json!([[12]])), true);
439 assert_eq!(less_than(&json!(2), &json!([10, [12]])), false);
440 }
441
442 #[test]
443 fn multi_elem_arrays() {
444 assert_eq!(less_than(&json!([1, 2]), &json!([3, 4])), true);
446 assert_eq!(less_than(&json!([3, 4]), &json!([1, 2])), false);
447 assert_eq!(less_than(&json!([1, 2, 2]), &json!([2, 2])), true);
448 }
449
450 #[test]
451 fn bool_number() {
452 assert_eq!(less_than(&json!(false), &json!(1)), true);
454 assert_eq!(less_than(&json!(true), &json!(1)), false);
455 assert_eq!(less_than(&json!(true), &json!(2)), true);
456
457 assert_eq!(less_than(&json!(-1), &json!(false)), true);
459 assert_eq!(less_than(&json!(1), &json!(true)), false);
460 assert_eq!(less_than(&json!(0), &json!(true)), true);
461 }
462
463 #[test]
464 fn bool_string() {
465 assert_eq!(less_than(&json!(false), &json!("1")), true);
467 assert_eq!(less_than(&json!(true), &json!("1")), false);
468 assert_eq!(less_than(&json!(true), &json!("2")), true);
469 assert_eq!(less_than(&json!(true), &json!("foo")), false);
470
471 assert_eq!(less_than(&json!("-1"), &json!(false)), true);
473 assert_eq!(less_than(&json!("1"), &json!(true)), false);
474 assert_eq!(less_than(&json!("0"), &json!(true)), true);
475 assert_eq!(less_than(&json!("foo"), &json!(true)), false);
476 }
477
478 #[test]
479 fn bool_array() {
480 less_than!(false, [true], false);
481 less_than!(false, [false], false);
482 less_than!(false, [0], false);
483 less_than!(false, [1], true);
484 less_than!(false, [1, 2], false);
485 less_than!(true, [true], false);
486 less_than!(true, [false], false);
487 less_than!(true, [0], false);
488 less_than!(true, [1], false);
489 less_than!(true, [2], true);
490 less_than!(true, [2, 3], false);
491 }
492
493 #[test]
494 fn string_array() {
495 assert_eq!(less_than(&json!([1]), &json!("12")), true);
496 assert_eq!(less_than(&json!([2]), &json!("12")), false);
497 }
498
499 #[test]
500 fn with_null() {
501 macro_rules! null_less_than {
503 ($a:expr, $b:expr) => {
504 assert_eq!(less_than(&json!(null), &json!($a)), $b);
505 };
506 }
507
508 macro_rules! is_less_than_null {
509 ($a:expr, $b:expr) => {
510 assert_eq!(less_than(&json!($a), &json!(null)), $b);
511 };
512 }
513
514 null_less_than!(1, true);
515 null_less_than!("5", true);
516 null_less_than!(true, true);
517
518 null_less_than!({}, false);
519 null_less_than!([-5], false);
520 null_less_than!(["-5"], false);
521 null_less_than!([5], true);
522 null_less_than!(["5"], true);
523
524 is_less_than_null!(-1, true);
525 is_less_than_null!(1, false);
526 is_less_than_null!("-1", true);
527 is_less_than_null!("1", false);
528
529 is_less_than_null!({}, false);
530 is_less_than_null!([-5], true);
531 is_less_than_null!(["-5"], true);
532 is_less_than_null!([5], false);
533 is_less_than_null!(["5"], false);
534 }
535 }
536
537 #[allow(clippy::approx_constant)]
538 mod parse_float {
539 use super::*;
540
541 #[test]
542 fn success() {
543 let result = Some(3.14);
544
545 assert_eq!(parse_float(&json!(3.14)), result);
546 assert_eq!(parse_float(&json!("3.14")), result);
547 assert_eq!(parse_float(&json!("3.14.5")), result);
548 assert_eq!(parse_float(&json!(" 3.14 ")), result);
549 assert_eq!(parse_float(&json!("314e-2")), result);
550 assert_eq!(parse_float(&json!("0.0314E+2")), result);
551 assert_eq!(parse_float(&json!("0.0314e+2")), result);
552 assert_eq!(parse_float(&json!("3.14some non-digit characters")), result);
553 }
554
555 #[test]
556 fn nan() {
557 assert_eq!(parse_float(&json!("FF2")), None);
558 }
559
560 #[test]
561 fn sign() {
562 assert_eq!(parse_float(&json!("+3.14")), Some(3.14));
563 assert_eq!(parse_float(&json!("-3.14")), Some(-3.14));
564 }
565 }
566
567 #[test]
568 fn test_less_equal_than() {
569 assert_eq!(less_equal_than(&json!(1), &json!(1)), true);
570 assert_eq!(less_equal_than(&json!([1]), &json!("1")), true);
571 assert_eq!(less_equal_than(&json!([1]), &json!("12")), true);
572
573 assert_eq!(less_equal_than(&json!(2), &json!(1)), false);
574 assert_eq!(less_equal_than(&json!([2]), &json!("12")), false);
575 }
576
577 #[test]
578 fn test_greater_than() {
579 assert_eq!(greater_than(&json!(2), &json!(1)), true);
580 assert_eq!(greater_than(&json!(2), &json!(2)), false);
581 assert_eq!(greater_than(&json!(2), &json!(3)), false);
582 }
583
584 #[test]
585 fn test_greater_equal_than() {
586 assert_eq!(greater_equal_than(&json!(2), &json!(1)), true);
587 assert_eq!(greater_equal_than(&json!(2), &json!(2)), true);
588 assert_eq!(greater_equal_than(&json!(2), &json!(3)), false);
589 }
590
591 #[test]
592 fn truthy_values() {
593 assert_eq!(is_truthy(&json!(0)), false);
595 assert_eq!(is_truthy(&json!(-1)), true);
596 assert_eq!(is_truthy(&json!(1)), true);
597 assert_eq!(is_truthy(&json!([])), false);
598 assert_eq!(is_truthy(&json!([1, 2])), true);
599 assert_eq!(is_truthy(&json!("")), false);
600 assert_eq!(is_truthy(&json!("anything")), true);
601 assert_eq!(is_truthy(&json!("0")), true);
602 assert_eq!(is_truthy(&json!(["0"])), true);
603 assert_eq!(is_truthy(&json!(null)), false);
604
605 assert_eq!(is_truthy(&json!({})), true);
606 assert_eq!(is_truthy(&json!(true)), true);
607 assert_eq!(is_truthy(&json!(false)), false);
608 }
609
610 #[test]
611 fn value_as_string_coercion() {
612 assert_eq!(coerce_to_str(&json!(true)), "true");
613 assert_eq!(coerce_to_str(&json!(false)), "false");
614 assert_eq!(coerce_to_str(&json!([false])), "false");
615 assert_eq!(coerce_to_str(&json!([true])), "true");
616 assert_eq!(coerce_to_str(&json!(null)), "null");
617 assert_eq!(coerce_to_str(&json!({})), "[object Object]");
618
619 assert_eq!(coerce_to_str(&json!([1, 2])), "1,2");
620 assert_eq!(coerce_to_str(&json!([[1, 2], [3, 4]])), "1,2,3,4");
622 assert_eq!(
624 coerce_to_str(&json!([[1, 2], [[true, 4]], 5])),
625 "1,2,true,4,5"
626 );
627 }
628
629 #[test]
630 fn value_as_f64_coercion() {
631 assert_eq!(coerce_to_f64(&json!([[[5]]])), Some(5f64));
632 assert_eq!(coerce_to_f64(&json!([[[5], 6]])), None);
633 assert_eq!(coerce_to_f64(&json!([[[1, 2]]])), None);
634 assert_eq!(coerce_to_f64(&json!(null)), Some(0f64));
635 assert_eq!(coerce_to_f64(&json!(true)), Some(1f64));
636 assert_eq!(coerce_to_f64(&json!([true])), None);
637 assert_eq!(coerce_to_f64(&json!(false)), Some(0f64));
638 assert_eq!(coerce_to_f64(&json!([false])), None);
639 assert_eq!(coerce_to_f64(&json!("1")), Some(1f64));
640 assert_eq!(coerce_to_f64(&json!("1.1")), Some(1.1));
641 assert_eq!(coerce_to_f64(&json!("1.8")), Some(1.8));
642 assert_eq!(coerce_to_f64(&json!(["1"])), Some(1f64));
643 assert_eq!(coerce_to_f64(&json!(null)), Some(0f64));
644 assert_eq!(coerce_to_f64(&json!([null])), Some(0f64));
645 }
646}