json_patch/
diff.rs

1use crate::Patch;
2use jsonptr::PointerBuf;
3use serde_json::{Map, Value};
4
5fn diff_impl(left: &Value, right: &Value, pointer: &mut PointerBuf, patch: &mut super::Patch) {
6    match (left, right) {
7        (Value::Object(ref left_obj), Value::Object(ref right_obj)) => {
8            diff_object(left_obj, right_obj, pointer, patch);
9        }
10        (Value::Array(ref left_array), Value::Array(ref ref_array)) => {
11            diff_array(left_array, ref_array, pointer, patch);
12        }
13        (_, _) if left == right => {
14            // Nothing to do
15        }
16        (_, _) => {
17            // Values are different, replace the value at the path
18            patch
19                .0
20                .push(super::PatchOperation::Replace(super::ReplaceOperation {
21                    path: pointer.clone(),
22                    value: right.clone(),
23                }));
24        }
25    }
26}
27
28fn diff_array(left: &[Value], right: &[Value], pointer: &mut PointerBuf, patch: &mut Patch) {
29    let len = left.len().max(right.len());
30    let mut shift = 0usize;
31    for idx in 0..len {
32        pointer.push_back(idx - shift);
33        match (left.get(idx), right.get(idx)) {
34            (Some(left), Some(right)) => {
35                // Both array have an element at this index
36                diff_impl(left, right, pointer, patch);
37            }
38            (Some(_left), None) => {
39                // The left array has an element at this index, but not the right
40                shift += 1;
41                patch
42                    .0
43                    .push(super::PatchOperation::Remove(super::RemoveOperation {
44                        path: pointer.clone(),
45                    }));
46            }
47            (None, Some(right)) => {
48                // The right array has an element at this index, but not the left
49                patch
50                    .0
51                    .push(super::PatchOperation::Add(super::AddOperation {
52                        path: pointer.clone(),
53                        value: right.clone(),
54                    }));
55            }
56            (None, None) => {
57                unreachable!()
58            }
59        }
60        pointer.pop_back();
61    }
62}
63
64fn diff_object(
65    left: &Map<String, Value>,
66    right: &Map<String, Value>,
67    pointer: &mut PointerBuf,
68    patch: &mut Patch,
69) {
70    // Add or replace keys in the right object
71    for (key, right_value) in right {
72        pointer.push_back(key);
73        match left.get(key) {
74            Some(left_value) => {
75                diff_impl(left_value, right_value, pointer, patch);
76            }
77            None => {
78                patch
79                    .0
80                    .push(super::PatchOperation::Add(super::AddOperation {
81                        path: pointer.clone(),
82                        value: right_value.clone(),
83                    }));
84            }
85        }
86        pointer.pop_back();
87    }
88
89    // Remove keys that are not in the right object
90    for key in left.keys() {
91        if !right.contains_key(key) {
92            pointer.push_back(key);
93            patch
94                .0
95                .push(super::PatchOperation::Remove(super::RemoveOperation {
96                    path: pointer.clone(),
97                }));
98            pointer.pop_back();
99        }
100    }
101}
102
103/// Diff two JSON documents and generate a JSON Patch (RFC 6902).
104///
105/// # Example
106/// Diff two JSONs:
107///
108/// ```rust
109/// #[macro_use]
110/// use json_patch::{Patch, patch, diff};
111/// use serde_json::{json, from_value};
112///
113/// # pub fn main() {
114/// let left = json!({
115///   "title": "Goodbye!",
116///   "author" : {
117///     "givenName" : "John",
118///     "familyName" : "Doe"
119///   },
120///   "tags":[ "example", "sample" ],
121///   "content": "This will be unchanged"
122/// });
123///
124/// let right = json!({
125///   "title": "Hello!",
126///   "author" : {
127///     "givenName" : "John"
128///   },
129///   "tags": [ "example" ],
130///   "content": "This will be unchanged",
131///   "phoneNumber": "+01-123-456-7890"
132/// });
133///
134/// let p = diff(&left, &right);
135/// assert_eq!(p, from_value::<Patch>(json!([
136///   { "op": "replace", "path": "/title", "value": "Hello!" },
137///   { "op": "remove", "path": "/author/familyName" },
138///   { "op": "remove", "path": "/tags/1" },
139///   { "op": "add", "path": "/phoneNumber", "value": "+01-123-456-7890" },
140/// ])).unwrap());
141///
142/// let mut doc = left.clone();
143/// patch(&mut doc, &p).unwrap();
144/// assert_eq!(doc, right);
145///
146/// # }
147/// ```
148pub fn diff(left: &Value, right: &Value) -> super::Patch {
149    let mut patch = super::Patch::default();
150    let mut path = PointerBuf::new();
151    diff_impl(left, right, &mut path, &mut patch);
152    patch
153}
154
155#[cfg(test)]
156mod tests {
157    use serde_json::{json, Value};
158
159    #[test]
160    pub fn replace_all() {
161        let mut left = json!({"title": "Hello!"});
162        let patch = super::diff(&left, &Value::Null);
163        assert_eq!(
164            patch,
165            serde_json::from_value(json!([
166                { "op": "replace", "path": "", "value": null },
167            ]))
168            .unwrap()
169        );
170        crate::patch(&mut left, &patch).unwrap();
171    }
172
173    #[test]
174    pub fn diff_empty_key() {
175        let mut left = json!({"title": "Something", "": "Hello!"});
176        let right = json!({"title": "Something", "": "Bye!"});
177        let patch = super::diff(&left, &right);
178        assert_eq!(
179            patch,
180            serde_json::from_value(json!([
181                { "op": "replace", "path": "/", "value": "Bye!" },
182            ]))
183            .unwrap()
184        );
185        crate::patch(&mut left, &patch).unwrap();
186        assert_eq!(left, right);
187    }
188
189    #[test]
190    pub fn add_all() {
191        let right = json!({"title": "Hello!"});
192        let patch = super::diff(&Value::Null, &right);
193        assert_eq!(
194            patch,
195            serde_json::from_value(json!([
196                { "op": "replace", "path": "", "value": { "title": "Hello!" } },
197            ]))
198            .unwrap()
199        );
200
201        let mut left = Value::Null;
202        crate::patch(&mut left, &patch).unwrap();
203        assert_eq!(left, right);
204    }
205
206    #[test]
207    pub fn remove_all() {
208        let mut left = json!(["hello", "bye"]);
209        let right = json!([]);
210        let patch = super::diff(&left, &right);
211        assert_eq!(
212            patch,
213            serde_json::from_value(json!([
214                { "op": "remove", "path": "/0" },
215                { "op": "remove", "path": "/0" },
216            ]))
217            .unwrap()
218        );
219
220        crate::patch(&mut left, &patch).unwrap();
221        assert_eq!(left, right);
222    }
223
224    #[test]
225    pub fn remove_tail() {
226        let mut left = json!(["hello", "bye", "hi"]);
227        let right = json!(["hello"]);
228        let patch = super::diff(&left, &right);
229        assert_eq!(
230            patch,
231            serde_json::from_value(json!([
232                { "op": "remove", "path": "/1" },
233                { "op": "remove", "path": "/1" },
234            ]))
235            .unwrap()
236        );
237
238        crate::patch(&mut left, &patch).unwrap();
239        assert_eq!(left, right);
240    }
241
242    #[test]
243    pub fn add_tail() {
244        let mut left = json!(["hello"]);
245        let right = json!(["hello", "bye", "hi"]);
246        let patch = super::diff(&left, &right);
247        assert_eq!(
248            patch,
249            serde_json::from_value(json!([
250                { "op": "add", "path": "/1", "value": "bye" },
251                { "op": "add", "path": "/2", "value": "hi" }
252            ]))
253            .unwrap()
254        );
255
256        crate::patch(&mut left, &patch).unwrap();
257        assert_eq!(left, right);
258    }
259
260    #[test]
261    pub fn replace_object() {
262        let mut left = json!(["hello", "bye"]);
263        let right = json!({"hello": "bye"});
264        let patch = super::diff(&left, &right);
265        assert_eq!(
266            patch,
267            serde_json::from_value(json!([
268                { "op": "replace", "path": "", "value": {"hello": "bye"} }
269            ]))
270            .unwrap()
271        );
272
273        crate::patch(&mut left, &patch).unwrap();
274        assert_eq!(left, right);
275    }
276
277    #[test]
278    fn escape_json_keys() {
279        let mut left = json!({
280            "/slashed/path/with/~": 1
281        });
282        let right = json!({
283            "/slashed/path/with/~": 2,
284        });
285        let patch = super::diff(&left, &right);
286
287        crate::patch(&mut left, &patch).unwrap();
288        assert_eq!(left, right);
289    }
290
291    #[test]
292    pub fn replace_object_array() {
293        let mut left = json!({ "style": { "ref": {"name": "name"} } });
294        let right = json!({ "style": [{ "ref": {"hello": "hello"} }]});
295        let patch = crate::diff(&left, &right);
296
297        assert_eq!(
298            patch,
299            serde_json::from_value(json!([
300                { "op": "replace", "path": "/style", "value": [{ "ref": {"hello": "hello"} }] },
301            ]))
302            .unwrap()
303        );
304        crate::patch(&mut left, &patch).unwrap();
305        assert_eq!(left, right);
306    }
307
308    #[test]
309    pub fn replace_array_object() {
310        let mut left = json!({ "style": [{ "ref": {"hello": "hello"} }]});
311        let right = json!({ "style": { "ref": {"name": "name"} } });
312        let patch = crate::diff(&left, &right);
313
314        assert_eq!(
315            patch,
316            serde_json::from_value(json!([
317                { "op": "replace", "path": "/style", "value": { "ref": {"name": "name"} } },
318            ]))
319            .unwrap()
320        );
321        crate::patch(&mut left, &patch).unwrap();
322        assert_eq!(left, right);
323    }
324
325    #[test]
326    pub fn remove_keys() {
327        let mut left = json!({"first": 1, "second": 2, "third": 3});
328        let right = json!({"first": 1, "second": 2});
329        let patch = super::diff(&left, &right);
330        assert_eq!(
331            patch,
332            serde_json::from_value(json!([
333                { "op": "remove", "path": "/third" }
334            ]))
335            .unwrap()
336        );
337
338        crate::patch(&mut left, &patch).unwrap();
339        assert_eq!(left, right);
340    }
341
342    #[test]
343    pub fn add_keys() {
344        let mut left = json!({"first": 1, "second": 2});
345        let right = json!({"first": 1, "second": 2, "third": 3});
346        let patch = super::diff(&left, &right);
347        assert_eq!(
348            patch,
349            serde_json::from_value(json!([
350                { "op": "add", "path": "/third", "value": 3 }
351            ]))
352            .unwrap()
353        );
354
355        crate::patch(&mut left, &patch).unwrap();
356        assert_eq!(left, right);
357    }
358}