Skip to main content

semdiff_differ_json/
lib.rs

1use crate::json_path::JsonPath;
2use crate::json_path::eval::{JsonPathMatchState, JsonPathMatcher};
3use mime::Mime;
4use semdiff_core::fs::FileLeaf;
5use semdiff_core::{Diff, DiffCalculator, MayUnsupported};
6use serde_json::Value;
7use similar::algorithms::DiffHook;
8use std::cmp::Reverse;
9use std::collections::BinaryHeap;
10use std::fmt::Display;
11use std::{convert, fmt, mem};
12
13pub mod json_path;
14pub mod report_html;
15pub mod report_json;
16pub mod report_summary;
17
18#[cfg(test)]
19mod tests;
20
21#[derive(Debug, Clone, Copy, Default)]
22pub struct JsonDiffReporter;
23
24#[derive(Debug)]
25enum JsonDiffBody {
26    Equal { body: String, ignored_lines: JsonDiffLines },
27    Modified(JsonDiffLines),
28}
29
30#[derive(Debug)]
31pub struct JsonDiff {
32    body: JsonDiffBody,
33}
34
35impl Diff for JsonDiff {
36    fn equal(&self) -> bool {
37        matches!(self.body, JsonDiffBody::Equal { .. })
38    }
39}
40
41impl JsonDiff {
42    fn body(&self) -> &JsonDiffBody {
43        &self.body
44    }
45}
46
47#[derive(Debug, Clone)]
48pub struct JsonDiffCalculator {
49    ignore_object_key_order: bool,
50    ignore_paths: Vec<JsonPath>,
51}
52
53impl Default for JsonDiffCalculator {
54    fn default() -> Self {
55        Self::new(false, Vec::new())
56    }
57}
58
59impl JsonDiffCalculator {
60    pub fn new(ignore_object_key_order: bool, ignore_paths: Vec<JsonPath>) -> Self {
61        Self {
62            ignore_object_key_order,
63            ignore_paths,
64        }
65    }
66
67    pub fn ignore_object_key_order(&self) -> bool {
68        self.ignore_object_key_order
69    }
70}
71
72impl DiffCalculator<FileLeaf> for JsonDiffCalculator {
73    type Error = convert::Infallible;
74    type Diff = JsonDiff;
75
76    fn diff(
77        &self,
78        _name: &str,
79        expected: FileLeaf,
80        actual: FileLeaf,
81    ) -> Result<MayUnsupported<Self::Diff>, Self::Error> {
82        if !is_json_mime(&expected.kind) || !is_json_mime(&actual.kind) {
83            return Ok(MayUnsupported::Unsupported);
84        }
85        let Ok(mut expected) = serde_json::from_slice::<Value>(&expected.content) else {
86            return Ok(MayUnsupported::Unsupported);
87        };
88        let Ok(mut actual) = serde_json::from_slice::<Value>(&actual.content) else {
89            return Ok(MayUnsupported::Unsupported);
90        };
91        if self.ignore_object_key_order {
92            expected.sort_all_objects();
93            actual.sort_all_objects();
94        }
95        let diff = json_diff(&expected, &actual, &self.ignore_paths);
96        let body = if diff.iter().all(JsonDiffLine::is_equal_for_result) {
97            let ignored_lines = if diff.iter().any(JsonDiffLine::is_ignored) {
98                diff
99            } else {
100                JsonDiffLines::default()
101            };
102            JsonDiffBody::Equal {
103                body: serde_json::to_string_pretty(&expected).unwrap(),
104                ignored_lines,
105            }
106        } else {
107            JsonDiffBody::Modified(diff)
108        };
109        let result = JsonDiff { body };
110        Ok(MayUnsupported::Ok(result))
111    }
112}
113
114fn is_json_mime(kind: &Mime) -> bool {
115    if kind == &mime::APPLICATION_JSON {
116        return true;
117    }
118    if kind.type_() == mime::APPLICATION
119        && let Some(suffix) = kind.subtype().as_str().strip_suffix("+json")
120    {
121        return !suffix.is_empty();
122    }
123    kind.essence_str() == "text/json"
124}
125
126fn try_into_json(content: &[u8]) -> Option<String> {
127    let value = serde_json::from_slice::<Value>(content).ok()?;
128    Some(serde_json::to_string_pretty(&value).unwrap())
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132enum ChangeTag {
133    Unchanged,
134    Ignored,
135    Added,
136    Deleted,
137}
138
139#[derive(Debug)]
140struct JsonDiffLine {
141    indent: usize,
142    state: JsonDiffLineState,
143}
144
145impl JsonDiffLine {
146    fn tag(&self) -> ChangeTag {
147        match self.state {
148            JsonDiffLineState::Unchanged { .. } => ChangeTag::Unchanged,
149            JsonDiffLineState::Ignored { .. } => ChangeTag::Ignored,
150            JsonDiffLineState::Added(_) => ChangeTag::Added,
151            JsonDiffLineState::Deleted(_) => ChangeTag::Deleted,
152        }
153    }
154
155    fn is_equal_for_result(&self) -> bool {
156        matches!(
157            self.state,
158            JsonDiffLineState::Unchanged { .. } | JsonDiffLineState::Ignored { .. }
159        )
160    }
161
162    fn is_ignored(&self) -> bool {
163        matches!(self.state, JsonDiffLineState::Ignored { .. })
164    }
165
166    fn has_expected(&self) -> bool {
167        !matches!(
168            self.state,
169            JsonDiffLineState::Added(_) | JsonDiffLineState::Ignored { expected: None, .. }
170        )
171    }
172
173    fn has_actual(&self) -> bool {
174        !matches!(
175            self.state,
176            JsonDiffLineState::Deleted(_) | JsonDiffLineState::Ignored { actual: None, .. }
177        )
178    }
179
180    fn preview_text(&self) -> &str {
181        match &self.state {
182            JsonDiffLineState::Unchanged { expected, .. } => expected,
183            JsonDiffLineState::Ignored { expected, actual } => {
184                expected.as_ref().or(actual.as_ref()).map_or("", String::as_str)
185            }
186            JsonDiffLineState::Added(actual) => actual,
187            JsonDiffLineState::Deleted(expected) => expected,
188        }
189    }
190
191    fn display_expected(&self) -> impl Display {
192        fmt::from_fn(|f| {
193            let expected = match &self.state {
194                JsonDiffLineState::Unchanged { expected, .. } => expected,
195                JsonDiffLineState::Ignored {
196                    expected: Some(expected),
197                    ..
198                } => expected,
199                JsonDiffLineState::Ignored { expected: None, .. } => return Ok(()),
200                JsonDiffLineState::Added(_) => return Ok(()),
201                JsonDiffLineState::Deleted(expected) => expected,
202            };
203            for _ in 0..self.indent {
204                f.write_str("  ")?;
205            }
206            f.write_str(expected)?;
207            Ok(())
208        })
209    }
210
211    fn display_actual(&self) -> impl Display {
212        fmt::from_fn(|f| {
213            let actual = match &self.state {
214                JsonDiffLineState::Unchanged { actual, .. } => actual,
215                JsonDiffLineState::Ignored {
216                    actual: Some(actual), ..
217                } => actual,
218                JsonDiffLineState::Ignored { actual: None, .. } => return Ok(()),
219                JsonDiffLineState::Added(actual) => actual,
220                JsonDiffLineState::Deleted(_) => return Ok(()),
221            };
222            for _ in 0..self.indent {
223                f.write_str("  ")?;
224            }
225            f.write_str(actual)?;
226            Ok(())
227        })
228    }
229}
230
231#[derive(Debug)]
232enum JsonDiffLineState {
233    Unchanged {
234        expected: String,
235        actual: String,
236    },
237    Ignored {
238        expected: Option<String>,
239        actual: Option<String>,
240    },
241    Added(String),
242    Deleted(String),
243}
244
245#[derive(Debug, Default)]
246struct JsonDiffLines {
247    lines: Vec<JsonDiffLine>,
248}
249
250impl JsonDiffLines {
251    fn writer(&mut self) -> JsonDiffLineWriter<'_> {
252        JsonDiffLineWriter {
253            lines: &mut self.lines,
254            indent: 0,
255        }
256    }
257}
258
259impl std::ops::Deref for JsonDiffLines {
260    type Target = [JsonDiffLine];
261
262    fn deref(&self) -> &Self::Target {
263        &self.lines
264    }
265}
266
267impl IntoIterator for JsonDiffLines {
268    type Item = JsonDiffLine;
269    type IntoIter = std::vec::IntoIter<JsonDiffLine>;
270
271    fn into_iter(self) -> Self::IntoIter {
272        self.lines.into_iter()
273    }
274}
275
276struct JsonDiffLineWriter<'a> {
277    lines: &'a mut Vec<JsonDiffLine>,
278    indent: usize,
279}
280
281#[derive(Clone, Copy)]
282struct RenderedJson<'a> {
283    body: &'a str,
284    prefix: Option<&'a str>,
285    trailing_comma: bool,
286}
287
288impl<'a> RenderedJson<'a> {
289    fn lines(self) -> RenderedJsonLines<'a> {
290        RenderedJsonLines {
291            lines: self.body.lines().peekable(),
292            prefix: self.prefix,
293            trailing_comma: self.trailing_comma,
294        }
295    }
296}
297
298struct RenderedJsonLines<'a> {
299    lines: std::iter::Peekable<std::str::Lines<'a>>,
300    prefix: Option<&'a str>,
301    trailing_comma: bool,
302}
303
304impl<'a> Iterator for RenderedJsonLines<'a> {
305    type Item = RenderedJsonLine<'a>;
306
307    fn next(&mut self) -> Option<Self::Item> {
308        let line = self.lines.next()?;
309        Some(RenderedJsonLine {
310            prefix: self.prefix.take(),
311            line,
312            trailing_comma: self.trailing_comma && self.lines.peek().is_none(),
313        })
314    }
315}
316
317struct RenderedJsonLine<'a> {
318    prefix: Option<&'a str>,
319    line: &'a str,
320    trailing_comma: bool,
321}
322
323impl Display for RenderedJsonLine<'_> {
324    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
325        if let Some(prefix) = self.prefix {
326            f.write_str(prefix)?;
327        }
328        f.write_str(self.line)?;
329        if self.trailing_comma {
330            f.write_str(",")?;
331        }
332        Ok(())
333    }
334}
335
336struct ClosingLine {
337    delimiter: char,
338    trailing_comma: bool,
339}
340
341impl Display for ClosingLine {
342    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343        write!(f, "{}", self.delimiter)?;
344        if self.trailing_comma {
345            f.write_str(",")?;
346        }
347        Ok(())
348    }
349}
350
351struct MemberContainerStart<'a> {
352    quoted_key: &'a str,
353    delimiter: char,
354}
355
356impl Display for MemberContainerStart<'_> {
357    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
358        f.write_str(self.quoted_key)?;
359        f.write_str(": ")?;
360        write!(f, "{}", self.delimiter)?;
361        Ok(())
362    }
363}
364
365impl JsonDiffLineWriter<'_> {
366    fn indent(&mut self) -> JsonDiffLineWriter<'_> {
367        JsonDiffLineWriter {
368            lines: &mut *self.lines,
369            indent: self.indent + 1,
370        }
371    }
372
373    fn unchanged_display(&mut self, expected: impl Display, actual: impl Display) {
374        self.lines.push(JsonDiffLine {
375            indent: self.indent,
376            state: JsonDiffLineState::Unchanged {
377                expected: expected.to_string(),
378                actual: actual.to_string(),
379            },
380        });
381    }
382
383    fn unchanged_same(&mut self, line: &'static str) {
384        self.lines.push(JsonDiffLine {
385            indent: self.indent,
386            state: JsonDiffLineState::Unchanged {
387                expected: line.to_owned(),
388                actual: line.to_owned(),
389            },
390        });
391    }
392
393    fn unchanged_same_display(&mut self, line: impl Display) {
394        let line = line.to_string();
395        self.lines.push(JsonDiffLine {
396            indent: self.indent,
397            state: JsonDiffLineState::Unchanged {
398                expected: line.clone(),
399                actual: line,
400            },
401        });
402    }
403
404    fn unchanged_value(&mut self, value: &Value, expected_trailing_comma: bool, actual_trailing_comma: bool) {
405        let body = serde_json::to_string_pretty(value).unwrap();
406        self.unchanged_rendered_pair(
407            RenderedJson {
408                body: &body,
409                prefix: None,
410                trailing_comma: expected_trailing_comma,
411            },
412            RenderedJson {
413                body: &body,
414                prefix: None,
415                trailing_comma: actual_trailing_comma,
416            },
417        );
418    }
419
420    fn unchanged_member(
421        &mut self,
422        key: &str,
423        value: &Value,
424        expected_trailing_comma: bool,
425        actual_trailing_comma: bool,
426    ) {
427        let body = serde_json::to_string_pretty(value).unwrap();
428        let prefix = Self::member_prefix(key);
429        self.unchanged_rendered_pair(
430            RenderedJson {
431                body: &body,
432                prefix: Some(&prefix),
433                trailing_comma: expected_trailing_comma,
434            },
435            RenderedJson {
436                body: &body,
437                prefix: Some(&prefix),
438                trailing_comma: actual_trailing_comma,
439            },
440        );
441    }
442
443    fn added_value(&mut self, value: &Value, trailing_comma: bool) {
444        let body = serde_json::to_string_pretty(value).unwrap();
445        self.added_rendered(RenderedJson {
446            body: &body,
447            prefix: None,
448            trailing_comma,
449        });
450    }
451
452    fn added_member(&mut self, key: &str, value: &Value, trailing_comma: bool) {
453        let body = serde_json::to_string_pretty(value).unwrap();
454        let prefix = Self::member_prefix(key);
455        self.added_rendered(RenderedJson {
456            body: &body,
457            prefix: Some(&prefix),
458            trailing_comma,
459        });
460    }
461
462    fn deleted_value(&mut self, value: &Value, trailing_comma: bool) {
463        let body = serde_json::to_string_pretty(value).unwrap();
464        self.deleted_rendered(RenderedJson {
465            body: &body,
466            prefix: None,
467            trailing_comma,
468        });
469    }
470
471    fn deleted_member(&mut self, key: &str, value: &Value, trailing_comma: bool) {
472        let body = serde_json::to_string_pretty(value).unwrap();
473        let prefix = Self::member_prefix(key);
474        self.deleted_rendered(RenderedJson {
475            body: &body,
476            prefix: Some(&prefix),
477            trailing_comma,
478        });
479    }
480
481    fn ignored_value(&mut self, expected: Option<(&Value, bool)>, actual: Option<(&Value, bool)>) {
482        let expected =
483            expected.map(|(value, trailing_comma)| (serde_json::to_string_pretty(value).unwrap(), trailing_comma));
484        let actual =
485            actual.map(|(value, trailing_comma)| (serde_json::to_string_pretty(value).unwrap(), trailing_comma));
486        self.ignored_rendered_pair(
487            expected.as_ref().map(|(body, trailing_comma)| RenderedJson {
488                body,
489                prefix: None,
490                trailing_comma: *trailing_comma,
491            }),
492            actual.as_ref().map(|(body, trailing_comma)| RenderedJson {
493                body,
494                prefix: None,
495                trailing_comma: *trailing_comma,
496            }),
497        );
498    }
499
500    fn ignored_member(&mut self, expected: Option<(&str, &Value, bool)>, actual: Option<(&str, &Value, bool)>) {
501        let expected = expected.map(|(key, value, trailing_comma)| {
502            (
503                serde_json::to_string_pretty(value).unwrap(),
504                Self::member_prefix(key),
505                trailing_comma,
506            )
507        });
508        let actual = actual.map(|(key, value, trailing_comma)| {
509            (
510                serde_json::to_string_pretty(value).unwrap(),
511                Self::member_prefix(key),
512                trailing_comma,
513            )
514        });
515        self.ignored_rendered_pair(
516            expected.as_ref().map(|(body, prefix, trailing_comma)| RenderedJson {
517                body,
518                prefix: Some(prefix),
519                trailing_comma: *trailing_comma,
520            }),
521            actual.as_ref().map(|(body, prefix, trailing_comma)| RenderedJson {
522                body,
523                prefix: Some(prefix),
524                trailing_comma: *trailing_comma,
525            }),
526        );
527    }
528
529    fn unchanged_rendered_pair(&mut self, expected: RenderedJson<'_>, actual: RenderedJson<'_>) {
530        for (expected, actual) in expected.lines().zip(actual.lines()) {
531            self.unchanged_display(expected, actual);
532        }
533    }
534
535    fn added_rendered(&mut self, rendered: RenderedJson<'_>) {
536        for line in rendered.lines() {
537            self.lines.push(JsonDiffLine {
538                indent: self.indent,
539                state: JsonDiffLineState::Added(line.to_string()),
540            });
541        }
542    }
543
544    fn deleted_rendered(&mut self, rendered: RenderedJson<'_>) {
545        for line in rendered.lines() {
546            self.lines.push(JsonDiffLine {
547                indent: self.indent,
548                state: JsonDiffLineState::Deleted(line.to_string()),
549            });
550        }
551    }
552
553    fn ignored_rendered_pair(&mut self, expected: Option<RenderedJson<'_>>, actual: Option<RenderedJson<'_>>) {
554        let mut expected_lines = expected.map(RenderedJson::lines);
555        let mut actual_lines = actual.map(RenderedJson::lines);
556        loop {
557            let expected_line = expected_lines.as_mut().and_then(Iterator::next);
558            let actual_line = actual_lines.as_mut().and_then(Iterator::next);
559            if expected_line.is_none() && actual_line.is_none() {
560                break;
561            }
562            self.lines.push(JsonDiffLine {
563                indent: self.indent,
564                state: JsonDiffLineState::Ignored {
565                    expected: expected_line.map(|line| line.to_string()),
566                    actual: actual_line.map(|line| line.to_string()),
567                },
568            });
569        }
570    }
571
572    fn member_prefix(key: &str) -> String {
573        let mut key = serde_json::to_string(key).unwrap();
574        key.push_str(": ");
575        key
576    }
577}
578
579fn json_diff(expected: &Value, actual: &Value, ignore_paths: &[JsonPath]) -> JsonDiffLines {
580    fn json_array_diff<'stack, 'path, 'value>(
581        expected: &'value [Value],
582        actual: &'value [Value],
583        expected_state: &mut JsonPathMatchState<'stack, 'path, &'value Value>,
584        actual_state: &mut JsonPathMatchState<'stack, 'path, &'value Value>,
585        writer: &mut JsonDiffLineWriter<'_>,
586    ) {
587        let mut hook = ArrayDiffHook {
588            expected,
589            actual,
590            expected_state,
591            actual_state,
592            writer,
593        };
594        similar::algorithms::patience::diff(
595            &mut similar::algorithms::Replace::new(&mut hook),
596            expected,
597            0..expected.len(),
598            actual,
599            0..actual.len(),
600        )
601        .unwrap();
602
603        struct ArrayDiffHook<'hook, 'value, 'stack, 'path, 'lines> {
604            expected: &'value [Value],
605            actual: &'value [Value],
606            expected_state: &'hook mut JsonPathMatchState<'stack, 'path, &'value Value>,
607            actual_state: &'hook mut JsonPathMatchState<'stack, 'path, &'value Value>,
608            writer: &'hook mut JsonDiffLineWriter<'lines>,
609        }
610
611        impl DiffHook for ArrayDiffHook<'_, '_, '_, '_, '_> {
612            type Error = convert::Infallible;
613
614            fn equal(&mut self, old_index: usize, new_index: usize, len: usize) -> Result<(), Self::Error> {
615                for (expected_index, actual_index) in (old_index..).zip(new_index..).take(len) {
616                    let need_extra_comma_expected = expected_index < self.expected.len() - 1;
617                    let need_extra_comma_actual = actual_index < self.actual.len() - 1;
618                    let v = &self.expected[expected_index];
619                    self.writer
620                        .unchanged_value(v, need_extra_comma_expected, need_extra_comma_actual);
621                }
622                Ok(())
623            }
624
625            fn delete(&mut self, old_index: usize, old_len: usize, _new_index: usize) -> Result<(), Self::Error> {
626                for i in (old_index..).take(old_len) {
627                    let need_extra_comma = i < self.expected.len() - 1;
628                    let v = &self.expected[i];
629                    let expected_state = self.expected_state.advance_index(i).unwrap();
630                    if expected_state.is_match() {
631                        self.writer.ignored_value(Some((v, need_extra_comma)), None);
632                        continue;
633                    }
634                    self.writer.deleted_value(v, need_extra_comma);
635                }
636                Ok(())
637            }
638
639            fn insert(&mut self, _old_index: usize, new_index: usize, new_len: usize) -> Result<(), Self::Error> {
640                for i in (new_index..).take(new_len) {
641                    let need_extra_comma = i < self.actual.len() - 1;
642                    let v = &self.actual[i];
643                    let actual_state = self.actual_state.advance_index(i).unwrap();
644                    if actual_state.is_match() {
645                        self.writer.ignored_value(None, Some((v, need_extra_comma)));
646                        continue;
647                    }
648                    self.writer.added_value(v, need_extra_comma);
649                }
650                Ok(())
651            }
652
653            fn replace(
654                &mut self,
655                old_index: usize,
656                old_len: usize,
657                new_index: usize,
658                new_len: usize,
659            ) -> Result<(), Self::Error> {
660                fn value_match_score(expected: &Value, actual: &Value) -> usize {
661                    if expected == actual {
662                        0
663                    } else {
664                        match (expected, actual) {
665                            (Value::Array(expected), Value::Array(actual)) => {
666                                10 + expected.len().abs_diff(actual.len())
667                            }
668                            (Value::Object(expected), Value::Object(actual)) => {
669                                10 + expected.keys().filter(|k| !actual.contains_key(k.as_str())).count()
670                                    + actual.keys().filter(|k| !expected.contains_key(k.as_str())).count()
671                            }
672                            _ if mem::discriminant::<Value>(expected) == mem::discriminant::<Value>(actual) => 100,
673                            _ => 1000,
674                        }
675                    }
676                }
677
678                let mut expected_to_actual = vec![None::<usize>; old_len];
679                let mut actual_to_expected = vec![None::<usize>; new_len];
680
681                let mut expected_ignored = Vec::with_capacity(old_len);
682                for expected_index in 0..old_len {
683                    let expected_state = self.expected_state.advance_index(old_index + expected_index).unwrap();
684                    expected_ignored.push(expected_state.is_match());
685                }
686                let mut actual_ignored = Vec::with_capacity(new_len);
687                for actual_index in 0..new_len {
688                    let actual_state = self.actual_state.advance_index(new_index + actual_index).unwrap();
689                    actual_ignored.push(actual_state.is_match());
690                }
691
692                let mut q = BinaryHeap::new();
693                for (expected_index, expected) in self.expected[old_index..][..old_len].iter().enumerate() {
694                    for (actual_index, actual) in self.actual[new_index..][..new_len].iter().enumerate() {
695                        q.push((
696                            Reverse(value_match_score(expected, actual)),
697                            expected_ignored[expected_index] == actual_ignored[actual_index],
698                            Reverse(expected_index.abs_diff(actual_index)),
699                            expected_index,
700                            actual_index,
701                        ));
702                    }
703                }
704                while let Some((_, _, _, expected_index, actual_index)) = q.pop() {
705                    if expected_to_actual[expected_index].is_some() || actual_to_expected[actual_index].is_some() {
706                        continue;
707                    }
708                    let requirements = [
709                        expected_to_actual[..expected_index]
710                            .iter()
711                            .rev()
712                            .find_map(Option::as_ref)
713                            .is_none_or(|&left| left < actual_index),
714                        expected_to_actual[expected_index..]
715                            .iter()
716                            .find_map(Option::as_ref)
717                            .is_none_or(|&right| actual_index < right),
718                        actual_to_expected[..actual_index]
719                            .iter()
720                            .rev()
721                            .find_map(Option::as_ref)
722                            .is_none_or(|&left| left < expected_index),
723                        actual_to_expected[actual_index..]
724                            .iter()
725                            .find_map(Option::as_ref)
726                            .is_none_or(|&right| expected_index < right),
727                    ];
728                    if requirements.into_iter().all(convert::identity) {
729                        expected_to_actual[expected_index] = Some(actual_index);
730                        actual_to_expected[actual_index] = Some(expected_index);
731                    }
732                }
733                let mut expected_to_actual = expected_to_actual.into_iter().enumerate().peekable();
734                let mut actual_to_expected = actual_to_expected.into_iter().enumerate().peekable();
735                let expected_index_base = old_index;
736                let actual_index_base = new_index;
737                loop {
738                    while let Some((expected_index, _)) =
739                        expected_to_actual.next_if(|(_, actual_index)| actual_index.is_none())
740                    {
741                        self.delete(expected_index_base + expected_index, 1, 0)?;
742                    }
743                    while let Some((actual_index, _)) =
744                        actual_to_expected.next_if(|(_, expected_index)| expected_index.is_none())
745                    {
746                        self.insert(0, actual_index_base + actual_index, 1)?;
747                    }
748                    match (expected_to_actual.next(), actual_to_expected.next()) {
749                        (None, None) => break,
750                        (Some((expected_index, Some(actual_index_))), Some((actual_index, Some(expected_index_)))) => {
751                            assert_eq!(expected_index, expected_index_);
752                            assert_eq!(actual_index, actual_index_);
753                            let expected_index = expected_index_base + expected_index;
754                            let actual_index = actual_index_base + actual_index;
755                            let need_extra_comma_expected = expected_index < self.expected.len() - 1;
756                            let need_extra_comma_actual = actual_index < self.actual.len() - 1;
757                            let expected_value = &self.expected[expected_index];
758                            let actual_value = &self.actual[actual_index];
759                            let mut expected_state = self.expected_state.advance_index(expected_index).unwrap();
760                            let mut actual_state = self.actual_state.advance_index(actual_index).unwrap();
761                            if expected_state.is_match() || actual_state.is_match() {
762                                self.writer.ignored_value(
763                                    Some((expected_value, need_extra_comma_expected)),
764                                    Some((actual_value, need_extra_comma_actual)),
765                                );
766                                continue;
767                            }
768                            if expected_value == actual_value {
769                                self.writer.unchanged_value(
770                                    expected_value,
771                                    need_extra_comma_expected,
772                                    need_extra_comma_actual,
773                                );
774                                continue;
775                            }
776                            match (expected_value, actual_value) {
777                                (Value::Array(expected), Value::Array(actual)) => {
778                                    self.writer.unchanged_same("[");
779                                    let mut result = self.writer.indent();
780                                    json_array_diff(
781                                        expected,
782                                        actual,
783                                        &mut expected_state,
784                                        &mut actual_state,
785                                        &mut result,
786                                    );
787                                    self.writer.unchanged_display(
788                                        ClosingLine {
789                                            delimiter: ']',
790                                            trailing_comma: need_extra_comma_expected,
791                                        },
792                                        ClosingLine {
793                                            delimiter: ']',
794                                            trailing_comma: need_extra_comma_actual,
795                                        },
796                                    );
797                                }
798                                (Value::Object(expected), Value::Object(actual)) => {
799                                    self.writer.unchanged_same("{");
800                                    let mut result = self.writer.indent();
801                                    json_object_diff(
802                                        expected,
803                                        actual,
804                                        &mut expected_state,
805                                        &mut actual_state,
806                                        &mut result,
807                                    );
808                                    self.writer.unchanged_display(
809                                        ClosingLine {
810                                            delimiter: '}',
811                                            trailing_comma: need_extra_comma_expected,
812                                        },
813                                        ClosingLine {
814                                            delimiter: '}',
815                                            trailing_comma: need_extra_comma_actual,
816                                        },
817                                    );
818                                }
819                                _ => {
820                                    drop(expected_state);
821                                    drop(actual_state);
822                                    self.delete(expected_index, 1, 0)?;
823                                    self.insert(0, actual_index, 1)?;
824                                }
825                            }
826                        }
827                        _ => unreachable!(),
828                    }
829                }
830                Ok(())
831            }
832        }
833    }
834    fn json_object_diff<'stack, 'path, 'value>(
835        expected: &'value serde_json::Map<String, Value>,
836        actual: &'value serde_json::Map<String, Value>,
837        expected_state: &mut JsonPathMatchState<'stack, 'path, &'value Value>,
838        actual_state: &mut JsonPathMatchState<'stack, 'path, &'value Value>,
839        writer: &mut JsonDiffLineWriter<'_>,
840    ) {
841        let expected_keys = expected.keys().collect::<Vec<_>>();
842        let actual_keys = actual.keys().collect::<Vec<_>>();
843        let mut hook = ObjectDiffHook {
844            expected,
845            actual,
846            expected_keys: &expected_keys,
847            actual_keys: &actual_keys,
848            expected_state,
849            actual_state,
850            writer,
851        };
852        similar::algorithms::patience::diff(
853            &mut hook,
854            &expected_keys,
855            0..expected_keys.len(),
856            &actual_keys,
857            0..actual_keys.len(),
858        )
859        .unwrap();
860
861        struct ObjectDiffHook<'hook, 'value, 'stack, 'path, 'lines> {
862            expected: &'value serde_json::Map<String, Value>,
863            actual: &'value serde_json::Map<String, Value>,
864            expected_keys: &'hook [&'value String],
865            actual_keys: &'hook [&'value String],
866            expected_state: &'hook mut JsonPathMatchState<'stack, 'path, &'value Value>,
867            actual_state: &'hook mut JsonPathMatchState<'stack, 'path, &'value Value>,
868            writer: &'hook mut JsonDiffLineWriter<'lines>,
869        }
870
871        impl DiffHook for ObjectDiffHook<'_, '_, '_, '_, '_> {
872            type Error = convert::Infallible;
873
874            fn equal(&mut self, old_index: usize, new_index: usize, len: usize) -> Result<(), Self::Error> {
875                assert_eq!(len, 1);
876                let need_extra_comma_expected = old_index < self.expected_keys.len() - 1;
877                let need_extra_comma_actual = new_index < self.actual_keys.len() - 1;
878                let k = self.expected_keys[old_index];
879                let expected_v = self.expected.get(k).unwrap();
880                let actual_v = self.actual.get(k).unwrap();
881                let mut expected_state = self.expected_state.advance_name(k).unwrap();
882                let mut actual_state = self.actual_state.advance_name(k).unwrap();
883                if (expected_state.is_match() || actual_state.is_match()) && expected_v != actual_v {
884                    self.writer.ignored_member(
885                        Some((k, expected_v, need_extra_comma_expected)),
886                        Some((k, actual_v, need_extra_comma_actual)),
887                    );
888                    return Ok(());
889                }
890                match (expected_v, actual_v) {
891                    (expected @ Value::Null, actual @ Value::Null)
892                    | (expected @ Value::Bool(_), actual @ Value::Bool(_))
893                    | (expected @ Value::Number(_), actual @ Value::Number(_))
894                    | (expected @ Value::String(_), actual @ Value::String(_))
895                        if expected == actual =>
896                    {
897                        self.writer
898                            .unchanged_member(k, expected, need_extra_comma_expected, need_extra_comma_actual);
899                    }
900                    (Value::Array(expected), Value::Array(actual)) => {
901                        let quoted_key = serde_json::to_string(k).unwrap();
902                        self.writer.unchanged_same_display(MemberContainerStart {
903                            quoted_key: &quoted_key,
904                            delimiter: '[',
905                        });
906                        let mut result = self.writer.indent();
907                        json_array_diff(expected, actual, &mut expected_state, &mut actual_state, &mut result);
908                        self.writer.unchanged_display(
909                            ClosingLine {
910                                delimiter: ']',
911                                trailing_comma: need_extra_comma_expected,
912                            },
913                            ClosingLine {
914                                delimiter: ']',
915                                trailing_comma: need_extra_comma_actual,
916                            },
917                        );
918                    }
919                    (Value::Object(expected), Value::Object(actual)) => {
920                        let quoted_key = serde_json::to_string(k).unwrap();
921                        self.writer.unchanged_same_display(MemberContainerStart {
922                            quoted_key: &quoted_key,
923                            delimiter: '{',
924                        });
925                        let mut result = self.writer.indent();
926                        json_object_diff(expected, actual, &mut expected_state, &mut actual_state, &mut result);
927                        self.writer.unchanged_display(
928                            ClosingLine {
929                                delimiter: '}',
930                                trailing_comma: need_extra_comma_expected,
931                            },
932                            ClosingLine {
933                                delimiter: '}',
934                                trailing_comma: need_extra_comma_actual,
935                            },
936                        );
937                    }
938                    _ => {
939                        drop(expected_state);
940                        drop(actual_state);
941                        self.delete(old_index, 1, 0)?;
942                        self.insert(0, new_index, 1)?;
943                    }
944                }
945                Ok(())
946            }
947
948            fn delete(&mut self, old_index: usize, old_len: usize, _new_index: usize) -> Result<(), Self::Error> {
949                assert_eq!(old_len, 1);
950                let need_extra_comma = old_index < self.expected.len() - 1;
951                let k = self.expected_keys[old_index];
952                let v = self.expected.get(k).unwrap();
953                let expected_state = self.expected_state.advance_name(k).unwrap();
954                if expected_state.is_match() {
955                    self.writer.ignored_member(Some((k, v, need_extra_comma)), None);
956                    return Ok(());
957                }
958                self.writer.deleted_member(k, v, need_extra_comma);
959                Ok(())
960            }
961
962            fn insert(&mut self, _old_index: usize, new_index: usize, new_len: usize) -> Result<(), Self::Error> {
963                assert_eq!(new_len, 1);
964                let need_extra_comma = new_index < self.actual.len() - 1;
965                let k = self.actual_keys[new_index];
966                let v = self.actual.get(k).unwrap();
967                let actual_state = self.actual_state.advance_name(k).unwrap();
968                if actual_state.is_match() {
969                    self.writer.ignored_member(None, Some((k, v, need_extra_comma)));
970                    return Ok(());
971                }
972                self.writer.added_member(k, v, need_extra_comma);
973                Ok(())
974            }
975
976            fn replace(
977                &mut self,
978                _old_index: usize,
979                _old_len: usize,
980                _new_index: usize,
981                _new_len: usize,
982            ) -> Result<(), Self::Error> {
983                unreachable!()
984            }
985        }
986    }
987    let mut expected_matcher = JsonPathMatcher::new(ignore_paths);
988    let mut actual_matcher = JsonPathMatcher::new(ignore_paths);
989    let mut expected_state = expected_matcher.root_state(expected);
990    let mut actual_state = actual_matcher.root_state(actual);
991    let mut result = JsonDiffLines::default();
992    let mut writer = result.writer();
993    if (expected_state.is_match() || actual_state.is_match()) && expected != actual {
994        writer.ignored_value(Some((expected, false)), Some((actual, false)));
995    } else {
996        match (expected, actual) {
997            (expected @ Value::Null, actual @ Value::Null)
998            | (expected @ Value::Bool(_), actual @ Value::Bool(_))
999            | (expected @ Value::Number(_), actual @ Value::Number(_))
1000            | (expected @ Value::String(_), actual @ Value::String(_)) => {
1001                if expected == actual {
1002                    writer.unchanged_value(expected, false, false);
1003                } else {
1004                    writer.deleted_value(expected, false);
1005                    writer.added_value(actual, false);
1006                }
1007            }
1008            (Value::Array(expected), Value::Array(actual)) => {
1009                writer.unchanged_same("[");
1010                let mut child = writer.indent();
1011                json_array_diff(expected, actual, &mut expected_state, &mut actual_state, &mut child);
1012                writer.unchanged_same("]");
1013            }
1014            (Value::Object(expected), Value::Object(actual)) => {
1015                writer.unchanged_same("{");
1016                let mut child = writer.indent();
1017                json_object_diff(expected, actual, &mut expected_state, &mut actual_state, &mut child);
1018                writer.unchanged_same("}");
1019            }
1020            (expected, actual) => {
1021                writer.deleted_value(expected, false);
1022                writer.added_value(actual, false);
1023            }
1024        }
1025    }
1026    result
1027}