1use crate::core_ext::{Indent, Indexes};
2use crate::{ArraySortingMode, CompareMode, Config, FloatCompareMode, NumericMode};
3use float_cmp::{ApproxEq, F64Margin, FloatMargin};
4use serde_json::Value;
5use std::{collections::HashSet, fmt};
6
7pub(crate) fn diff<'a>(
8 lhs: &'a Value,
9 rhs: &'a Value,
10 config: &'a Config,
11) -> Vec<DifferenceRef<'a>> {
12 let mut acc = vec![];
13 diff_with(lhs, rhs, config, PathRef::Root, &mut acc);
14 acc
15}
16
17fn diff_with<'a>(
18 lhs: &'a Value,
19 rhs: &'a Value,
20 config: &'a Config,
21 path: PathRef<'a>,
22 acc: &mut Vec<DifferenceRef<'a>>,
23) {
24 let mut folder = DiffFolder {
25 rhs,
26 path,
27 acc,
28 config,
29 };
30
31 fold_json(lhs, &mut folder);
32}
33
34#[derive(Debug)]
35struct DiffFolder<'a, 'b> {
36 rhs: &'a Value,
37 path: PathRef<'a>,
38 acc: &'b mut Vec<DifferenceRef<'a>>,
39 config: &'a Config,
40}
41
42macro_rules! direct_compare {
43 ($name:ident) => {
44 fn $name(&mut self, lhs: &'a Value) {
45 if self.rhs != lhs {
46 self.acc.push(DifferenceRef {
47 lhs: Some(lhs),
48 rhs: Some(&self.rhs),
49 path: self.path.clone(),
50 config: self.config.clone(),
51 });
52 }
53 }
54 };
55}
56
57impl<'a> DiffFolder<'a, '_> {
58 direct_compare!(on_null);
59 direct_compare!(on_bool);
60 direct_compare!(on_string);
61
62 fn on_number(&mut self, lhs: &'a Value) {
63 let is_equal = match self.config.numeric_mode {
64 NumericMode::Strict => self.eq_values(lhs, self.rhs),
65 NumericMode::AssumeFloat => match (lhs.as_f64(), self.rhs.as_f64()) {
66 (Some(lhs), Some(rhs)) => self.eq_floats(lhs, rhs),
67 (lhs, rhs) => lhs == rhs,
68 },
69 };
70 if !is_equal {
71 self.acc.push(DifferenceRef {
72 lhs: Some(lhs),
73 rhs: Some(self.rhs),
74 path: self.path.clone(),
75 config: self.config.clone(),
76 });
77 }
78 }
79
80 fn eq_values(&self, lhs: &Value, rhs: &Value) -> bool {
81 if lhs.is_f64() && rhs.is_f64() {
82 self.eq_floats(
85 lhs.as_f64().expect("float value"),
86 rhs.as_f64().expect("float value"),
87 )
88 } else {
89 lhs == rhs
90 }
91 }
92
93 fn eq_floats(&self, lhs: f64, rhs: f64) -> bool {
94 if let FloatCompareMode::Epsilon(epsilon) = self.config.float_compare_mode {
95 lhs.approx_eq(rhs, F64Margin::default().epsilon(epsilon))
96 } else {
97 lhs == rhs
98 }
99 }
100 fn on_array_contains(&mut self, lhs: &'a Value) {
101 if let Some(rhs) = self.rhs.as_array() {
102 let lhs_array = lhs.as_array().unwrap();
103
104 let lhs_len = lhs_array.len();
105 let rhs_len = rhs.len();
106
107 if self.config.compare_mode == CompareMode::Strict && lhs_len != rhs_len {
108 self.acc.push(DifferenceRef {
109 lhs: Some(lhs),
110 rhs: Some(self.rhs),
111 path: self.path.clone(),
112 config: self.config.clone(),
113 });
114 return;
115 }
116
117 for rhs_item in rhs.iter() {
118 let rhs_item_count = rhs
121 .iter()
122 .filter(|i| diff(rhs_item, i, self.config).is_empty())
123 .count();
124 let lhs_matching_items_count = lhs_array
127 .iter()
128 .filter(|lhs_item| diff(lhs_item, rhs_item, self.config).is_empty())
129 .count();
130 if lhs_matching_items_count < rhs_item_count {
131 self.acc.push(DifferenceRef {
132 lhs: Some(lhs),
133 rhs: Some(self.rhs),
134 path: self.path.clone(),
135 config: self.config.clone(),
136 });
137 break;
138 }
139 }
140 } else {
141 self.acc.push(DifferenceRef {
142 lhs: Some(lhs),
143 rhs: Some(self.rhs),
144 path: self.path.clone(),
145 config: self.config.clone(),
146 });
147 }
148 }
149
150 fn on_array(&mut self, lhs: &'a Value) {
151 if self.config.array_sorting_mode == ArraySortingMode::Ignore {
152 return self.on_array_contains(lhs);
153 }
154
155 if let Some(rhs) = self.rhs.as_array() {
156 let lhs = lhs.as_array().unwrap();
157
158 match self.config.compare_mode {
159 CompareMode::Inclusive => {
160 for (idx, rhs) in rhs.iter().enumerate() {
161 let path = self.path.append(KeyRef::Idx(idx));
162
163 if let Some(lhs) = lhs.get(idx) {
164 diff_with(lhs, rhs, self.config, path, self.acc)
165 } else {
166 self.acc.push(DifferenceRef {
167 lhs: None,
168 rhs: Some(self.rhs),
169 path,
170 config: self.config.clone(),
171 });
172 }
173 }
174 }
175 CompareMode::Strict => {
176 let all_keys = rhs
177 .indexes()
178 .into_iter()
179 .chain(lhs.indexes())
180 .collect::<HashSet<_>>();
181 for key in all_keys {
182 let path = self.path.append(KeyRef::Idx(key));
183
184 match (lhs.get(key), rhs.get(key)) {
185 (Some(lhs), Some(rhs)) => {
186 diff_with(lhs, rhs, self.config, path, self.acc);
187 }
188 (None, Some(rhs)) => {
189 self.acc.push(DifferenceRef {
190 lhs: None,
191 rhs: Some(rhs),
192 path,
193 config: self.config.clone(),
194 });
195 }
196 (Some(lhs), None) => {
197 self.acc.push(DifferenceRef {
198 lhs: Some(lhs),
199 rhs: None,
200 path,
201 config: self.config.clone(),
202 });
203 }
204 (None, None) => {
205 unreachable!("at least one of the maps should have the key")
206 }
207 }
208 }
209 }
210 }
211 } else {
212 self.acc.push(DifferenceRef {
213 lhs: Some(lhs),
214 rhs: Some(self.rhs),
215 path: self.path.clone(),
216 config: self.config.clone(),
217 });
218 }
219 }
220
221 fn on_object(&mut self, lhs: &'a Value) {
222 if let Some(rhs) = self.rhs.as_object() {
223 let lhs = lhs.as_object().unwrap();
224
225 match self.config.compare_mode {
226 CompareMode::Inclusive => {
227 for (key, rhs) in rhs.iter() {
228 let path = self.path.append(KeyRef::Field(key));
229
230 if let Some(lhs) = lhs.get(key) {
231 diff_with(lhs, rhs, self.config, path, self.acc)
232 } else {
233 self.acc.push(DifferenceRef {
234 lhs: None,
235 rhs: Some(self.rhs),
236 path,
237 config: self.config.clone(),
238 });
239 }
240 }
241 }
242 CompareMode::Strict => {
243 let all_keys = rhs.keys().chain(lhs.keys()).collect::<HashSet<_>>();
244 for key in all_keys {
245 let path = self.path.append(KeyRef::Field(key));
246
247 match (lhs.get(key), rhs.get(key)) {
248 (Some(lhs), Some(rhs)) => {
249 diff_with(lhs, rhs, self.config, path, self.acc);
250 }
251 (None, Some(rhs)) => {
252 self.acc.push(DifferenceRef {
253 lhs: None,
254 rhs: Some(rhs),
255 path,
256 config: self.config.clone(),
257 });
258 }
259 (Some(lhs), None) => {
260 self.acc.push(DifferenceRef {
261 lhs: Some(lhs),
262 rhs: None,
263 path,
264 config: self.config.clone(),
265 });
266 }
267 (None, None) => {
268 unreachable!("at least one of the maps should have the key")
269 }
270 }
271 }
272 }
273 }
274 } else {
275 self.acc.push(DifferenceRef {
276 lhs: Some(lhs),
277 rhs: Some(self.rhs),
278 path: self.path.clone(),
279 config: self.config.clone(),
280 });
281 }
282 }
283}
284
285#[derive(Debug, PartialEq, Clone)]
287pub struct Difference {
288 path: Path,
289 lhs: Option<Value>,
290 rhs: Option<Value>,
291 config: Config,
292}
293
294impl Difference {
295 pub fn path(&self) -> &Path {
297 &self.path
298 }
299
300 pub fn actual(&self) -> &Option<Value> {
302 &self.lhs
303 }
304
305 pub fn expected(&self) -> &Option<Value> {
307 &self.rhs
308 }
309
310 pub fn config(&self) -> &Config {
312 &self.config
313 }
314}
315
316impl<'a> From<DifferenceRef<'a>> for Difference {
317 fn from(diff: DifferenceRef<'a>) -> Self {
318 Difference {
319 path: Path::from(diff.path),
320 lhs: diff.lhs.cloned(),
321 rhs: diff.rhs.cloned(),
322 config: diff.config.clone(),
323 }
324 }
325}
326
327#[derive(Debug, PartialEq)]
328pub(crate) struct DifferenceRef<'a> {
329 path: PathRef<'a>,
330 lhs: Option<&'a Value>,
331 rhs: Option<&'a Value>,
332 config: Config,
333}
334
335impl fmt::Display for DifferenceRef<'_> {
336 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
337 let json_to_string = |json: &Value| serde_json::to_string_pretty(json).unwrap();
338
339 match (&self.config.compare_mode, &self.lhs, &self.rhs) {
340 (CompareMode::Inclusive, Some(actual), Some(expected)) => {
341 writeln!(f, "json atoms at path \"{}\" are not equal:", self.path)?;
342 writeln!(f, " expected:")?;
343 writeln!(f, "{}", json_to_string(expected).indent(8))?;
344 writeln!(f, " actual:")?;
345 write!(f, "{}", json_to_string(actual).indent(8))?;
346 }
347 (CompareMode::Inclusive, None, Some(_expected)) => {
348 write!(
349 f,
350 "json atom at path \"{}\" is missing from actual",
351 self.path
352 )?;
353 }
354 (CompareMode::Inclusive, Some(_actual), None) => {
355 unreachable!("stuff missing actual wont produce an error")
356 }
357 (CompareMode::Inclusive, None, None) => unreachable!("can't both be missing"),
358
359 (CompareMode::Strict, Some(lhs), Some(rhs)) => {
360 writeln!(f, "json atoms at path \"{}\" are not equal:", self.path)?;
361 writeln!(f, " lhs:")?;
362 writeln!(f, "{}", json_to_string(lhs).indent(8))?;
363 writeln!(f, " rhs:")?;
364 write!(f, "{}", json_to_string(rhs).indent(8))?;
365 }
366 (CompareMode::Strict, None, Some(_)) => {
367 write!(f, "json atom at path \"{}\" is missing from lhs", self.path)?;
368 }
369 (CompareMode::Strict, Some(_), None) => {
370 write!(f, "json atom at path \"{}\" is missing from rhs", self.path)?;
371 }
372 (CompareMode::Strict, None, None) => unreachable!("can't both be missing"),
373 }
374
375 Ok(())
376 }
377}
378
379#[derive(Debug, Clone, PartialEq)]
381pub enum Path {
382 Root,
384 Keys(Vec<Key>),
386}
387
388impl<'a> From<PathRef<'a>> for Path {
389 fn from(path: PathRef<'a>) -> Self {
390 match path {
391 PathRef::Root => Path::Root,
392 PathRef::Keys(keys) => Path::Keys(keys.into_iter().map(Key::from).collect()),
393 }
394 }
395}
396
397#[derive(Debug, Clone, PartialEq)]
398enum PathRef<'a> {
399 Root,
400 Keys(Vec<KeyRef<'a>>),
401}
402
403impl<'a> PathRef<'a> {
404 fn append(&self, next: KeyRef<'a>) -> PathRef<'a> {
405 match self {
406 PathRef::Root => PathRef::Keys(vec![next]),
407 PathRef::Keys(list) => {
408 let mut copy = list.clone();
409 copy.push(next);
410 PathRef::Keys(copy)
411 }
412 }
413 }
414}
415
416impl fmt::Display for PathRef<'_> {
417 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
418 match self {
419 PathRef::Root => write!(f, "(root)"),
420 PathRef::Keys(keys) => {
421 for key in keys {
422 write!(f, "{}", key)?;
423 }
424 Ok(())
425 }
426 }
427 }
428}
429
430#[derive(Debug, Clone, PartialEq, Eq)]
432pub enum Key {
433 Idx(usize),
435 Field(String),
437}
438
439impl<'a> From<KeyRef<'a>> for Key {
440 fn from(key: KeyRef<'a>) -> Self {
441 match key {
442 KeyRef::Idx(idx) => Key::Idx(idx),
443 KeyRef::Field(field) => Key::Field(field.to_owned()),
444 }
445 }
446}
447
448#[derive(Debug, Copy, Clone, PartialEq)]
449enum KeyRef<'a> {
450 Idx(usize),
451 Field(&'a str),
452}
453
454impl fmt::Display for KeyRef<'_> {
455 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
456 match self {
457 KeyRef::Idx(idx) => write!(f, "[{}]", idx),
458 KeyRef::Field(key) => write!(f, ".{}", key),
459 }
460 }
461}
462
463fn fold_json<'a>(json: &'a Value, folder: &mut DiffFolder<'a, '_>) {
464 match json {
465 Value::Null => folder.on_null(json),
466 Value::Bool(_) => folder.on_bool(json),
467 Value::Number(_) => folder.on_number(json),
468 Value::String(_) => folder.on_string(json),
469 Value::Array(_) => folder.on_array(json),
470 Value::Object(_) => folder.on_object(json),
471 }
472}
473
474#[cfg(test)]
475mod test {
476 #[allow(unused_imports)]
477 use super::*;
478 use serde_json::json;
479
480 #[test]
481 fn test_diffing_leaf_json() {
482 let config = Config::new(CompareMode::Inclusive);
483 let diffs = diff(&json!(null), &json!(null), &config);
484 assert_eq!(diffs, vec![]);
485
486 let diffs = diff(&json!(false), &json!(false), &config);
487 assert_eq!(diffs, vec![]);
488
489 let diffs = diff(&json!(true), &json!(true), &config);
490 assert_eq!(diffs, vec![]);
491
492 let diffs = diff(&json!(false), &json!(true), &config);
493 assert_eq!(diffs.len(), 1);
494
495 let diffs = diff(&json!(true), &json!(false), &config);
496 assert_eq!(diffs.len(), 1);
497
498 let actual = json!(1);
499 let expected = json!(1);
500 let diffs = diff(&actual, &expected, &config);
501 assert_eq!(diffs, vec![]);
502
503 let actual = json!(2);
504 let expected = json!(1);
505 let diffs = diff(&actual, &expected, &config);
506 assert_eq!(diffs.len(), 1);
507
508 let actual = json!(1);
509 let expected = json!(2);
510 let diffs = diff(&actual, &expected, &config);
511 assert_eq!(diffs.len(), 1);
512
513 let actual = json!(1.0);
514 let expected = json!(1.0);
515 let diffs = diff(&actual, &expected, &config);
516 assert_eq!(diffs, vec![]);
517
518 let actual = json!(1);
519 let expected = json!(1.0);
520 let diffs = diff(&actual, &expected, &config);
521 assert_eq!(diffs.len(), 1);
522
523 let actual = json!(1.0);
524 let expected = json!(1);
525 let diffs = diff(&actual, &expected, &config);
526 assert_eq!(diffs.len(), 1);
527
528 let config_assume_float = config.numeric_mode(NumericMode::AssumeFloat);
529
530 let actual = json!(1);
531 let expected = json!(1.0);
532 let diffs = diff(&actual, &expected, &config_assume_float);
533 assert_eq!(diffs, vec![]);
534
535 let actual = json!(1.0);
536 let expected = json!(1);
537 let diffs = diff(&actual, &expected, &config_assume_float);
538 assert_eq!(diffs, vec![]);
539
540 let actual = json!(1.15);
541 let expected = json!(1);
542 let config = Config::new(CompareMode::Inclusive)
543 .numeric_mode(NumericMode::AssumeFloat)
544 .float_compare_mode(FloatCompareMode::Epsilon(0.2));
545 let diffs = diff(&actual, &expected, &config);
546 assert_eq!(diffs, vec![]);
547
548 let actual = json!(1.25);
549 let expected = json!(1);
550 let config = Config::new(CompareMode::Inclusive)
551 .numeric_mode(NumericMode::AssumeFloat)
552 .float_compare_mode(FloatCompareMode::Epsilon(0.2));
553
554 let diffs = diff(&actual, &expected, &config);
555 assert_eq!(diffs.len(), 1);
556
557 let actual = json!(2);
558 let expected = json!(1);
559 let config =
560 Config::new(CompareMode::Inclusive).float_compare_mode(FloatCompareMode::Epsilon(2.0));
561
562 let diffs = diff(&actual, &expected, &config);
563 assert_eq!(diffs.len(), 1);
564 }
565
566 #[test]
567 fn test_diffing_array() {
568 let config = Config::new(CompareMode::Inclusive);
569 let actual = json!([]);
571 let expected = json!([]);
572 let diffs = diff(&actual, &expected, &config);
573 assert_eq!(diffs, vec![]);
574
575 let actual = json!([1]);
576 let expected = json!([]);
577 let diffs = diff(&actual, &expected, &config);
578 assert_eq!(diffs.len(), 0);
579
580 let actual = json!([]);
581 let expected = json!([1]);
582 let diffs = diff(&actual, &expected, &config);
583 assert_eq!(diffs.len(), 1);
584
585 let actual = json!([1]);
587 let expected = json!([1]);
588 let diffs = diff(&actual, &expected, &config);
589 assert_eq!(diffs, vec![]);
590
591 let actual = json!([1, 2]);
593 let expected = json!([1]);
594 let diffs = diff(&actual, &expected, &config);
595 assert_eq!(diffs, vec![]);
596
597 let actual = json!([1]);
599 let expected = json!([1, 2]);
600 let diffs = diff(&actual, &expected, &config);
601 assert_eq!(diffs.len(), 1);
602
603 let actual = json!([1, 3]);
605 let expected = json!([1, 2]);
606 let diffs = diff(&actual, &expected, &config);
607 assert_eq!(diffs.len(), 1);
608
609 let actual = json!(1);
611 let expected = json!([1]);
612 let diffs = diff(&actual, &expected, &config);
613 assert_eq!(diffs.len(), 1);
614
615 let actual = json!([1]);
616 let expected = json!(1);
617 let diffs = diff(&actual, &expected, &config);
618 assert_eq!(diffs.len(), 1);
619 }
620
621 #[test]
622 fn test_array_strict() {
623 let config = Config::new(CompareMode::Strict);
624 let actual = json!([]);
625 let expected = json!([]);
626 let diffs = diff(&actual, &expected, &config);
627 assert_eq!(diffs.len(), 0);
628
629 let actual = json!([1, 2]);
630 let expected = json!([1, 2]);
631 let diffs = diff(&actual, &expected, &config);
632 assert_eq!(diffs.len(), 0);
633
634 let actual = json!([1]);
635 let expected = json!([1, 2]);
636 let diffs = diff(&actual, &expected, &config);
637 assert_eq!(diffs.len(), 1);
638
639 let actual = json!([1, 2]);
640 let expected = json!([1]);
641 let diffs = diff(&actual, &expected, &config);
642 assert_eq!(diffs.len(), 1);
643 }
644
645 #[test]
646 fn test_object() {
647 let config = Config::new(CompareMode::Inclusive);
648 let actual = json!({});
649 let expected = json!({});
650 let diffs = diff(&actual, &expected, &config);
651 assert_eq!(diffs, vec![]);
652
653 let actual = json!({ "a": 1 });
654 let expected = json!({ "a": 1 });
655 let diffs = diff(&actual, &expected, &config);
656 assert_eq!(diffs, vec![]);
657
658 let actual = json!({ "a": 1, "b": 123 });
659 let expected = json!({ "a": 1 });
660 let diffs = diff(&actual, &expected, &config);
661 assert_eq!(diffs, vec![]);
662
663 let actual = json!({ "a": 1 });
664 let expected = json!({ "b": 1 });
665 let diffs = diff(&actual, &expected, &config);
666 assert_eq!(diffs.len(), 1);
667
668 let actual = json!({ "a": 1 });
669 let expected = json!({ "a": 2 });
670 let diffs = diff(&actual, &expected, &config);
671 assert_eq!(diffs.len(), 1);
672
673 let actual = json!({ "a": { "b": true } });
674 let expected = json!({ "a": {} });
675 let diffs = diff(&actual, &expected, &config);
676 assert_eq!(diffs, vec![]);
677 }
678
679 #[test]
680 fn test_object_strict() {
681 let config = Config::new(CompareMode::Strict);
682 let lhs = json!({});
683 let rhs = json!({ "a": 1 });
684 let diffs = diff(&lhs, &rhs, &config);
685 assert_eq!(diffs.len(), 1);
686
687 let lhs = json!({ "a": 1 });
688 let rhs = json!({});
689 let diffs = diff(&lhs, &rhs, &config);
690 assert_eq!(diffs.len(), 1);
691
692 let json = json!({ "a": 1 });
693 let diffs = diff(&json, &json, &config);
694 assert_eq!(diffs, vec![]);
695 }
696}