Crates.io | difficient |
lib.rs | difficient |
version | 0.1.0 |
created_at | 2025-07-24 10:38:39.177217+00 |
updated_at | 2025-07-24 10:38:39.177217+00 |
description | Efficient, type-safe, zero-allocation structural diffing |
homepage | http://github.com/redbadger/difficient |
repository | http://github.com/redbadger/difficient |
max_upload_size | |
id | 1765901 |
size | 89,903 |
Efficient, type-safe, (almost) zero-allocation structural diffing.
struct
or enum
with #[derive(Diffable)]
Visit
the diff, generating a custom stream of change eventsThe possibilities are endless!
use difficient::{Diffable, DeepDiff, AtomicDiff, PatchOnlyDiff, Id, Replace};
// here is the type we would like to diff - note `#[derive(Diffable)]`
#[derive(Diffable, PartialEq, Debug, Clone)]
enum SimpleEnum {
First,
Second { x: &'static str, y: SimpleStruct }, // SimpleStruct defined below
}
// create an initial value
let mut source = SimpleEnum::First;
// diffing a value against itself results in no change
let diff1 = source.diff(&source);
assert!(diff1.is_unchanged());
// create a second value and diff
let mut target1 = SimpleEnum::Second {
x: "hello",
y: SimpleStruct { a: "aaa".into(), b: 123, c: vec![1.23] }
};
let diff2 = source.diff(&target1);
// the value is fully replaced by a different enum variant
let expect_diff = DeepDiff::Replaced(&target1);
assert_eq!(diff2, expect_diff);
// 'applying' a diff to source value results in the target value
source.apply(&diff2).unwrap();
assert_eq!(source, target1);
// create a third value and diff
let target2 = SimpleEnum::Second {
x: "goodbye",
y: SimpleStruct { a: "aaa".into(), b: 234, c: vec![1.23] }
};
let diff3 = target1.diff(&target2);
// here the variant is patched but not totally replaced
let expect_diff = DeepDiff::Patched(SimpleEnumDiff::Second {
x: AtomicDiff::Replaced(&"goodbye"),
y: PatchOnlyDiff::Patched(
SimpleStructDiff { a: AtomicDiff::Unchanged, b: AtomicDiff::Replaced(&234) }
)
});
assert_eq!(diff3, expect_diff);
target1.apply(&diff3).unwrap();
assert_eq!(target1, target2);
// It works for structs too!
#[derive(Diffable, PartialEq, Debug, Clone)]
struct SimpleStruct {
a: String,
b: i32,
#[diffable(skip)] // do not take `c` field into account when diffing
c: Vec<f64>,
}
let mut source = SimpleStruct {
a: "Hello".into(),
b: 123,
c: vec![3.3, 2.2, 1.1],
};
let target = SimpleStruct {
a: "Hello".into(),
b: 123,
c: vec![5.5, 4.4, 3.3],
};
let diff = source.diff(&target);
assert!(diff.is_unchanged()); // unchanged because `c` is ignored
With the visitor
feature activated, we can generate a series of discrete changes from the Diff
type.
See examples/visitor.rs
for a worked example.
The diff function does not allocate, in most cases. This makes it extremely fast.
The exceptions are
Vec
, HashMap
or BTreeMap
)Then it may have to allocate, which makes it merely very fast.