Skip to main content

json_traits/
diff.rs

1use std::cmp::Ordering;
2use std::collections::BTreeMap;
3
4use serde_json::Value;
5
6use crate::{JsonPaths, JsonScalar};
7
8/// Compares JSON values, or maps of paths, by leaf value.
9///
10/// This is the Rust form of `DiffWith`. The pair is `(self, other)`.
11/// [`JsonScalar::Undefined`] fills the side where a path is absent.
12pub trait DiffJson {
13    /// Paths whose leaf values differ.
14    ///
15    /// Paths that are equal on both sides are omitted. JSON `null` equals
16    /// JSON `null`.
17    ///
18    /// # Examples
19    ///
20    /// ```
21    /// use json_traits::{DiffJson, JsonScalar};
22    /// use serde_json::json;
23    ///
24    /// let left = json!({ "name": "Stewie", "number": 12 });
25    /// let right = json!({ "name": "Stewie", "number": 13, "isAwesome": true });
26    ///
27    /// let diff = left.diff_with(&right);
28    /// assert_eq!(
29    ///     diff.get("number"),
30    ///     Some(&(JsonScalar::from_integer(12), JsonScalar::from_integer(13)))
31    /// );
32    /// assert_eq!(
33    ///     diff.get("isAwesome"),
34    ///     Some(&(JsonScalar::Undefined, JsonScalar::from(true)))
35    /// );
36    /// assert!(!diff.contains_key("name"));
37    /// ```
38    #[doc(alias = "DiffWith")]
39    #[must_use]
40    fn diff_with(&self, other: &Self) -> BTreeMap<String, (JsonScalar, JsonScalar)>;
41}
42
43impl DiffJson for Value {
44    fn diff_with(&self, other: &Self) -> BTreeMap<String, (JsonScalar, JsonScalar)> {
45        self.paths_and_values().diff_with(&other.paths_and_values())
46    }
47}
48
49impl DiffJson for BTreeMap<String, JsonScalar> {
50    fn diff_with(&self, other: &Self) -> BTreeMap<String, (JsonScalar, JsonScalar)> {
51        // Both maps are already ordered, so walk them together. Equal leaves
52        // are skipped without cloning their paths.
53        let mut diff = BTreeMap::new();
54        let mut left_keys = self.iter();
55        let mut right_keys = other.iter();
56        let mut left = left_keys.next();
57        let mut right = right_keys.next();
58        loop {
59            match (left, right) {
60                (None, None) => break,
61                (Some((key, value)), None) => {
62                    diff.insert(key.clone(), (value.clone(), JsonScalar::Undefined));
63                    left = left_keys.next();
64                }
65                (None, Some((key, value))) => {
66                    diff.insert(key.clone(), (JsonScalar::Undefined, value.clone()));
67                    right = right_keys.next();
68                }
69                (Some((left_key, left_value)), Some((right_key, right_value))) => {
70                    match left_key.cmp(right_key) {
71                        Ordering::Less => {
72                            diff.insert(
73                                left_key.clone(),
74                                (left_value.clone(), JsonScalar::Undefined),
75                            );
76                            left = left_keys.next();
77                        }
78                        Ordering::Greater => {
79                            diff.insert(
80                                right_key.clone(),
81                                (JsonScalar::Undefined, right_value.clone()),
82                            );
83                            right = right_keys.next();
84                        }
85                        Ordering::Equal => {
86                            if left_value != right_value {
87                                diff.insert(
88                                    left_key.clone(),
89                                    (left_value.clone(), right_value.clone()),
90                                );
91                            }
92                            left = left_keys.next();
93                            right = right_keys.next();
94                        }
95                    }
96                }
97            }
98        }
99        diff
100    }
101}