Crate difficient

Source
Expand description

§Difficient

Efficient, type-safe, (almost) zero-allocation structural diffing.

  • Annotate your struct or enum with #[derive(Diffable)]
  • Diff your value with another value of the same type
  • Apply the diff!
  • Serialize the diff!
  • Visit the diff, generating a custom stream of change events

The possibilities are endless!

§Example Usage

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

§Visitor

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.

§Efficient?

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, HashMap or BTreeMap)

Then it may have to allocate, which makes it merely very fast.

Structs§

Id

Enums§

ApplyError
AtomicDiff
A generic type which can represent two possible ‘diff’ states. Appropriate for primitive types which cannot be ‘partially’ changed (e.g. bool, i32) c.f. DeepDiff which can also represent a partially-changed state
DeepDiff
A generic type which can represent three possible ‘diff’ states. Appropriate for primitive types which can be ‘partially’ changed (e.g. structs) c.f. AtomicDiff which cannot represent a Patched state
KvDiff
A generic type which represents the possible change-states of a Key-Value type (e.g. HashMap, BTreeMap)
PatchOnlyDiff
A generic type which can represent two possible ‘diff’ states. Appropriate for types which cannot be fully replaced - this only really makes sense in the context of a derived ‘diff’ that has skipped fields
VecChange
VecDiff

Traits§

Apply
A trait that must implemented by any type that results from a ‘diff’ operation. It allows the (child) ‘diff’ type to be ‘applied’ to the parent type, mutating the parent in place to result in the value which is was originally diffed against.
DiffKey
A trait that must be implemented by any type T that is part of a Vec<T> being diffed.
Diffable
The core trait of this library. Enables a value ‘A’ to be ‘diffed’ against another value ‘B’ of the same type, returning a ‘diff’ representing the difference between the two. The diff can then be applied to A to result in B.
Replace
A trait that must be implemented by any type that results from a ‘diff’ operation.

Derive Macros§

Diffable