Diff
facet-diff provides comprehensive diffing capabilities for any type that implements Facet. It includes compact and tree formats with syntax highlighting and confusable character detection.
Struct field changes
Changes to multiple fields in a struct including nested settings.
settings.theme: "dark" → "light" age: 30 → 31 email: "alice@example.com" → "alice@newdomain.com"
{ .. 1 unchanged field age: 30 → 31 email: "alice@example.com" → "alice@newdomain.com" settings: { .. 1 unchanged field theme: "dark" → "light" } }
Nested structures
Changes to fields deep within nested structures.
sections.[0].heading: "Intro" → "Introduction"
sections.[1].content: "Some content here" → "Updated content"Sequences (lists/arrays)
Various operations on sequences including single element changes, insertions, deletions, and reordering.
a) Single element change: [2]: 3 → 99b) Insertions and deletions: [ - 1 - 2 + 1 + 4 + 5 .. 1 unchanged item ]
c) Reordering: [ + "c" .. 2 unchanged items - "c" ]
Enums
Enum diffing including same variant with different data and different variants.
a) Same variant, different data: ::Inactive.reason: "vacation" → "sick leave"
b) Different variants: Status::Active → Status::Pending { since: 42, }
Options
Option types including inner value changes and None to Some transitions.
a) Some to Some (inner change): email: "bob@example.com" → "bob@company.com" settings.notifications: false → true
b) None to Some: None → Some(42)
Many changes (truncated)
Large number of changes that get truncated to show summary.
[2]: 2 → 200
[4]: 4 → 400
[6]: 6 → 600
[8]: 8 → 800
[10]: 10 → 1000
[12]: 12 → 1200
[14]: 14 → 1400
[16]: 16 → 1600
[18]: 18 → 1800
[20]: 20 → 2000
... and 4 more changesNo changes
Comparing a value with itself shows no differences.
(no changes)Scalar types
Diffing primitive types including integers, floats, booleans, characters, and strings.
a) Integers: i32: 42 → -42 i128 min→max: -170141183460469231731687303715884105728 → 170141183460469231731687303715884105727 u64 0→max: 0 → 18446744073709551615b) Floats: f64: 3.141592653589793 → 2.718281828459045 f64 inf→-inf: inf → -inf f64 NaN→NaN: NaN → NaN
c) Booleans: bool: true → false
d) Characters: char: A → Z emoji: 🦀 → 🐍
e) Strings: &str: "hello" → "world" String unicode: "Hello 世界" → "Hello 🌍"
Confusable strings
Detection of Unicode confusable characters using the Unicode TR39 confusables database. These include homoglyphs that look similar but are from different scripts.
a) Latin 'a' vs Cyrillic 'а' (detected): "abc" → "аbc" (strings are visually confusable but differ in 1 position): [0]: 'a' (U+0061) vs '\u{0430}'b) Latin 'o' vs Greek 'ο' (detected): "foo" → "fοo" (strings are visually confusable but differ in 1 position): [1]: 'o' (U+006F) vs '\u{03BF}'
c) Latin 'e' vs Cyrillic 'е' (detected): "hello" → "hеllo" (strings are visually confusable but differ in 1 position): [1]: 'e' (U+0065) vs '\u{0435}'
d) With zero-width joiner (not in TR39): "test" → "test"
e) Different quote styles (not in TR39): r""quoted"" → "“quoted”"
f) Greek Iota vs Latin I (not in TR39): "userId" → "userΙd"
Byte slices
Diffing byte arrays including ASCII and binary data.
a) ASCII bytes: [ - 104 - 101 - 108 - 108 + 119 .. 1 unchanged item + 114 + 108 + 100 ]b) Binary data: [ - 0 - 255 - 66 - 19 + 0 + 254 + 66 + 55 ]
c) Vec<u8>: [2]: 3 → 99
Deep tree (6 levels)
Deeply nested structures demonstrating change detection at multiple nesting levels.
a) Change at deepest level (level 6): inner.inner.inner.inner.inner.tag: "original" → "modified" inner.inner.inner.inner.inner.value: 42 → 999b) Changes at multiple levels (2, 4, 6): inner.inner.inner.enabled: true → false inner.inner.inner.inner.inner.value: 42 → 100 inner.priority: 1 → 5
c) Changes at every level: label: "label-old" → "label-new" inner.priority: 1 → 2 inner.inner.name: "old" → "new" inner.inner.inner.enabled: true → false inner.inner.inner.inner.count: 10 → 20 inner.inner.inner.inner.inner.tag: "a" → "b" inner.inner.inner.inner.inner.value: 1 → 2
d) Tree format for deep change: { .. 1 unchanged field inner: { .. 1 unchanged field inner: { .. 1 unchanged field inner: { .. 1 unchanged field inner: { .. 1 unchanged field inner: { .. 1 unchanged field value: 42 → 999 } } } } } }
Wide tree (20 fields)
Structure with many fields demonstrating diff truncation and summarization.
a) Single field change (among 20 fields): field_18: 300 → 999b) Scattered changes (fields 2, 8, 14, 19): field_19: 400 → 888 field_14: true → false field_08: 3 → 999 field_02: "b" → "CHANGED"
c) Many changes (exceeds truncation limit): field_16: 100 → 1000 field_06: 1 → 10 field_02: "b" → "B" field_18: 300 → 3000 field_11: true → false field_01: "a" → "A" field_14: true → false field_20: 500 → 5000 field_05: "e" → "E" field_08: 3 → 30 ... and 10 more changes
d) Tree format with few changes: { .. 19 unchanged fields field_12: true → false }