A lightweight, zero-dependency struct diffing library which allows changed fields to be collected and applied. Derive Difference on a struct, then use the StructDiff trait to make and apply diffs. Supports optional serialization of the generated diff types with serde or nanoserde for ease of use.
use structdiff::{Difference,StructDiff};#[derive(Debug,PartialEq,Clone,Difference)]structExample{field1:f64,#[difference(skip)]field2:Vec<i32>,#[difference(collection_strategy="unordered_array_like")]field3:BTreeSet<usize>,}let first = Example{field1:0.0,field2:vec![],field3:vec![1,2,3].into_iter().collect(),};let second = Example{field1:3.14,field2:vec![1],field3:vec![2,3,4].into_iter().collect(),};let diffs = first.diff(&second);// diffs is now a Vec of differences between the two instances, // with length equal to number of changed/unskipped fieldsassert_eq!(diffs.len(),2);let diffed = first.apply(diffs);// diffed is now equal to second, except for skipped fieldassert_eq!(diffed.field1, second.field1);assert_eq!(&diffed.field3,&second.field3);assert_ne!(diffed, second);For more examples take a look at integration tests
- Field level
#[difference(skip)]- Do not consider this field when creating a diff#[difference(recurse)]- Generate a StructDiff for this field when creating a diff#[difference(collection_strategy = {})]"ordered_array_like"- Generates a minimal changeset for ordered, array-like collections of items which implementPartialEq. (uses levenshtein difference)"unordered_array_like"- Generates a minimal changeset for unordered, array-like collections of items which implementHash + Eq."unordered_map_like"- Generates a minimal changeset for unordered, map-like collections for which the key implementsHash + Eq.
#[difference(map_equality = {})]- Used withunordered_map_like"key_only"- only replace a key-value pair for which the key has changed"key_and_value"- replace a key-value pair if either the key or value has changed
#[difference(setter)]- Generate setters for this struct field#[difference(setter_name = {})]- Use this name instead of the default value when generating a setter for this field (used on field)
- Struct Level
#[difference(setters)]- Generate setters for all fields in the struct- Example: for the
field1of theExamplestruct used above, a function with the signatureset_field1_with_diff(&mut self, value: Option<usize>) -> Option<<Self as StructDiff>::Diff>will be generated. Useful when a single field will be changed in a struct with many fields, as it saves the comparison of all other fields.
- Example: for the
#[difference(expose)]/#[difference(expose = "MyDiffTypeName")]- expose the generated difference type (optionally, with the specified name)
- [
nanoserde,serde] - Serialization ofDifferencederived associated types. Allows diffs to easily be sent over network. debug_diffs- DeriveDebugon the generated diff typegenerated_setters- Enable generation of setters for struct fields. These setters automatically return a diff if a field's value is changed by the assignment.rustc_hash- Use the (non-cryptographic) hash implementation from therustc-hashcrate instead of the default hasher. Much faster diff generation for collections at the cost of a dependency.syn- Use asyn-based backend for theDifferencederive macro. This adds thesyn,quote, andproc-macro2dependencies, but supports Rust syntax throughsyn's parser instead of structdiff's zero-dependency derive parser.
This is being used actively for my own projects, although it's mostly working now. PRs will be accepted for either more tests or functionality.