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.
email: "alice@example.com" → "alice@newdomain.com"
age: 30 → 31
settings.theme: "dark" → "light"
{
.. 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 → 99
b) 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):
settings.notifications: false → true
email: "bob@example.com" → "bob@company.com"
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 changes
No 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 → 18446744073709551615
b) 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.value: 42 → 999
inner.inner.inner.inner.inner.tag: "original" → "modified"
b) Changes at multiple levels (2, 4, 6):
inner.priority: 1 → 5
inner.inner.inner.inner.inner.value: 42 → 100
inner.inner.inner.enabled: true → false
c) Changes at every level:
inner.inner.inner.enabled: true → false
inner.inner.inner.inner.inner.tag: "a" → "b"
inner.inner.inner.inner.inner.value: 1 → 2
inner.inner.inner.inner.count: 10 → 20
inner.inner.name: "old" → "new"
inner.priority: 1 → 2
label: "label-old" → "label-new"
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 → 999
b) Scattered changes (fields 2, 8, 14, 19):
field_08: 3 → 999
field_14: true → false
field_02: "b" → "CHANGED"
field_19: 400 → 888
c) Many changes (exceeds truncation limit):
field_15: true → false
field_19: 400 → 4000
field_11: true → false
field_05: "e" → "E"
field_03: "c" → "C"
field_13: true → false
field_09: 4 → 40
field_18: 300 → 3000
field_14: true → false
field_04: "d" → "D"
... and 10 more changes
d) Tree format with few changes:
{
.. 19 unchanged fields
field_12: true → false
}