Skip to main content

apif_assert/
diff.rs

1use console::Style;
2use dissimilar::{Chunk, diff};
3use serde_json::Value;
4use std::fmt::Write;
5
6/// Generates a colored diff between two JSON values
7pub fn get_json_diff(expected: &Value, actual: &Value) -> String {
8    let expected_str =
9        serde_json::to_string_pretty(expected).unwrap_or_else(|_| expected.to_string());
10    let actual_str = serde_json::to_string_pretty(actual).unwrap_or_else(|_| actual.to_string());
11
12    let diff_chunks = diff(&expected_str, &actual_str);
13
14    let mut output = String::new();
15    let _ = writeln!(output, "Diff (Expected - / Actual +):");
16
17    for chunk in diff_chunks {
18        match chunk {
19            Chunk::Equal(text) => {
20                let style = Style::new().dim();
21                let _ = write!(output, "{}", style.apply_to(text));
22            }
23            Chunk::Delete(text) => {
24                let style = Style::new().red();
25                let _ = write!(output, "{}", style.apply_to(text));
26            }
27            Chunk::Insert(text) => {
28                let style = Style::new().green();
29                let _ = write!(output, "{}", style.apply_to(text));
30            }
31        }
32    }
33
34    output
35}
36
37#[cfg(test)]
38mod tests {
39    use super::*;
40    use serde_json::json;
41
42    #[test]
43    fn test_get_json_diff() {
44        let expected = json!({
45            "name": "Alice",
46            "age": 30
47        });
48        let actual = json!({
49            "name": "Bob",
50            "age": 30
51        });
52
53        let diff = get_json_diff(&expected, &actual);
54        println!("{}", diff);
55
56        // The output format is now character-based diff without +/- prefixes for partial lines.
57        assert!(diff.contains("Alice"));
58        assert!(diff.contains("Bob"));
59        assert!(diff.contains("\"age\": 30"));
60    }
61}