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 }
16 (_, _) => {
17 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 diff_impl(left, right, pointer, patch);
37 }
38 (Some(_left), None) => {
39 shift += 1;
41 patch
42 .0
43 .push(super::PatchOperation::Remove(super::RemoveOperation {
44 path: pointer.clone(),
45 }));
46 }
47 (None, Some(right)) => {
48 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 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 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
103pub 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}