Efficient, type-safe, (almost) zero-allocation structural diffing.
- Annotate your
structorenumwith#[derive(Diffable)] - Diff your value with another value of the same type
- Apply the diff!
- Serialize the diff!
Visitthe diff, generating a custom stream of change events
The 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)]enumSimpleEnum{First,Second{x:&'staticstr,y:SimpleStruct},// SimpleStruct defined below}// create an initial valueletmut source = SimpleEnum::First;// diffing a value against itself results in no changelet diff1 = source.diff(&source);assert!(diff1.is_unchanged());// create a second value and diffletmut 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 variantlet 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 difflet 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 replacedlet 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)]structSimpleStruct{a:String,b:i32,#[diffable(skip)]// do not take `c` field into account when diffingc:Vec<f64>,}letmut 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 ignoredWith 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
- when dealing with boxed types
- when dealing collections (
Vec,HashMaporBTreeMap)
Then it may have to allocate, which makes it merely very fast.