testing/
json.rs

1use serde_json::Value;
2
3fn normalize_value_recursively(v: &mut Value, normalize: &mut dyn FnMut(&str, &mut Value)) {
4    match v {
5        Value::Array(arr) => {
6            for v in arr {
7                normalize_value_recursively(v, normalize);
8            }
9        }
10
11        Value::Object(obj) => {
12            for (k, v) in obj.iter_mut() {
13                normalize(k, v);
14            }
15
16            for (_, v) in obj.iter_mut() {
17                normalize_value_recursively(v, normalize);
18            }
19        }
20
21        _ => {}
22    }
23}
24
25/// Remove common properties, recursively. You can optionally normalize more by
26/// passing a closure.
27///
28/// Closure takes `(key, value)`.
29///
30/// Returns `true` if `actual` and `expected` are equal.
31pub fn diff_json_value(
32    a: &mut Value,
33    b: &mut Value,
34    normalize: &mut dyn FnMut(&str, &mut Value),
35) -> bool {
36    normalize_value_recursively(a, normalize);
37    normalize_value_recursively(b, normalize);
38
39    remove_common(a, b)
40}
41
42fn remove_common(a: &mut Value, b: &mut Value) -> bool {
43    if *a == *b {
44        return true;
45    }
46
47    match (&mut *a, &mut *b) {
48        (Value::Object(a), Value::Object(b)) => {
49            if a.is_empty() && b.is_empty() {
50                return true;
51            }
52
53            a.retain(|key, a_v| {
54                if let Some(b_v) = b.get_mut(key) {
55                    if remove_common(a_v, b_v) {
56                        // Remove from both
57                        b.remove(key);
58                        return false;
59                    }
60                } else {
61                    // Remove if a.foo is null and b does not have foo
62                    if a_v == &Value::Null {
63                        return false;
64                    }
65                }
66
67                // Preserve by default
68                true
69            });
70            b.retain(|key, b_v| {
71                // Remove if b.foo is null and a does not have foo
72                if b_v == &Value::Null && !a.contains_key(key) {
73                    return false;
74                }
75
76                // Preserve by default
77
78                true
79            });
80        }
81
82        (Value::Array(a), Value::Array(b)) => {
83            if a.len() == b.len() {
84                for (a_v, b_v) in a.iter_mut().zip(b.iter_mut()) {
85                    remove_common(a_v, b_v);
86                }
87            }
88        }
89
90        _ => {}
91    }
92
93    false
94}