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.
age: 30 → 31
settings.theme: "dark" → "light"
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 → 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):
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 → 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 → 99Deep 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 → 999
b) Changes at multiple levels (2, 4, 6):
inner.inner.inner.inner.inner.value: 42 → 100
inner.inner.inner.enabled: true → false
inner.priority: 1 → 5
c) Changes at every level:
label: "label-old" → "label-new"
inner.priority: 1 → 2
inner.inner.inner.enabled: true → false
inner.inner.inner.inner.count: 10 → 20
inner.inner.inner.inner.inner.value: 1 → 2
inner.inner.inner.inner.inner.tag: "a" → "b"
inner.inner.name: "old" → "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_02: "b" → "CHANGED"
field_19: 400 → 888
field_08: 3 → 999
field_14: true → false
c) Many changes (exceeds truncation limit):
field_02: "b" → "B"
field_05: "e" → "E"
field_08: 3 → 30
field_18: 300 → 3000
field_09: 4 → 40
field_11: true → false
field_01: "a" → "A"
field_14: true → false
field_17: 200 → 2000
field_13: true → false
... and 10 more changes
d) Tree format with few changes:
{
.. 19 unchanged fields
field_12: true → false
}