daft/
lib.rs

1#![cfg_attr(doc_cfg, feature(doc_cfg))]
2#![warn(missing_docs)]
3#![cfg_attr(not(feature = "std"), no_std)]
4
5//! Daft is a library to perform semantic diffs of Rust data structures.
6//!
7//! Daft consists of a trait called [`Diffable`], along with [a derive
8//! macro][macro@Diffable] by the same name. This trait represents the
9//! notion of a type for which two members can be simultaneously compared.
10//!
11//! # Features
12//!
13//! - **Recursive diffing** of structs, sets, and maps
14//! - **Derive macro** for automatically generating diff types
15//! - Choose between **eager** and **lazy** diffing
16//! - **No-std compatible**, both with and without `alloc`
17//!
18//! # Usage
19//!
20//! ```rust
21//! # #[cfg(all(feature = "std", feature = "derive"))] {
22//! use daft::{Diffable, Leaf};
23//!
24//! // Annotate your struct with `#[derive(Diffable)]`:
25//! #[derive(Diffable)]
26//! struct MyStruct {
27//!     a: i32,
28//!     b: String,
29//! }
30//!
31//! // This generates a type called MyStructDiff, which looks like:
32//! # /*
33//! #[automatically_derived]
34//! struct MyStructDiff<'daft> {
35//!     a: Leaf<&'daft i32>,
36//!     b: Leaf<&'daft str>,
37//! }
38//! # */
39//!
40//! // Then, with two instances of MyStruct:
41//! let before = MyStruct { a: 1, b: "hello".to_owned() };
42//! let after = MyStruct { a: 2, b: "world".to_owned() };
43//!
44//! // You can diff them like so:
45//! let diff = before.diff(&after);
46//!
47//! // And compare the results:
48//! assert_eq!(*diff.a.before, 1);
49//! assert_eq!(*diff.a.after, 2);
50//! assert_eq!(diff.b.before, "hello");
51//! assert_eq!(diff.b.after, "world");
52//! # }
53//! ```
54//!
55//! This crate assigns one side the name *before*, and the other side *after*.
56//! These labels are arbitrary: if *before* and *after* are swapped, the diff is
57//! reversed.
58//!
59//! ## Diff types
60//!
61//! Currently, daft comes with a few kinds of diff types:
62//!
63//! ### [`Leaf`] instances
64//!
65//! A [`Leaf`] represents a logical *leaf node* or *base case* in a diff, i.e. a
66//! point at which diffing stops. [`Leaf`] instances are used for:
67//!
68//! * *Scalar* or *primitive types* like `i32`, `String`, `bool`, etc.
69//! * *Enums*, since diffing across variants is usually not meaningful.
70//! * Vector and slice types, since there are several reasonable ways to diff
71//!   vectors (e.g. set-like, ordered, etc.) and we don't want to make assumptions.
72//! * As an opt-in mechanism for struct fields: see
73//!   [*Recursive diffs*](#recursive-diffs) below for more.
74//!
75//! #### Example
76//!
77//! A contrived example for integers:
78//!
79//! ```rust
80//! use daft::{Diffable, Leaf};
81//!
82//! let diff: Leaf<&i32> = 1_i32.diff(&2);
83//! assert_eq!(*diff.before, 1);
84//! assert_eq!(*diff.after, 2);
85//! ```
86//!
87//! Enums also use `Leaf`:
88//!
89//! ```rust
90//! use daft::{Diffable, Leaf};
91//!
92//! // Option<T> uses Leaf:
93//! let diff: Leaf<Option<&i32>> = Some(1_i32).diff(&Some(2));
94//! assert_eq!(diff.before, Some(&1));
95//! assert_eq!(diff.after, Some(&2));
96//!
97//! # #[cfg(feature = "derive")] {
98//! // Automatically derived enums also use Leaf:
99//! #[derive(Debug, PartialEq, Eq, Diffable)]
100//! enum MyEnum {
101//!     A(i32),
102//!     B(String),
103//! }
104//!
105//! let before = MyEnum::A(1);
106//! let after = MyEnum::B("hello".to_string());
107//!
108//! let diff: Leaf<&MyEnum> = before.diff(&after);
109//! assert_eq!(diff.before, &before);
110//! assert_eq!(diff.after, &after);
111//! # }
112//! ```
113//!
114//! Vectors use `Leaf` as well:
115//!
116//! ```rust
117//! # #[cfg(feature = "std")] {
118//! use daft::{Diffable, Leaf};
119//!
120//! let before = vec![1, 2, 3];
121//! let after = vec![4, 5, 6];
122//! let diff: Leaf<&[i32]> = before.diff(&after);
123//! assert_eq!(diff.before, &before);
124//! assert_eq!(diff.after, &after);
125//! # }
126//! ```
127//!
128//! ### Map diffs
129//!
130//! For [`BTreeMap`] and [`HashMap`], daft has corresponding [`BTreeMapDiff`]
131//! and [`HashMapDiff`] types. These types have fields for *common*, *added*,
132//! and *removed* entries.
133//!
134//! Map diffs are performed eagerly for keys, but values are stored as leaf
135//! nodes.
136//!
137//! #### Example
138//!
139//! ```rust
140//! # #[cfg(feature = "std")] {
141//! use daft::{Diffable, Leaf, BTreeMapDiff};
142//! use std::collections::BTreeMap;
143//!
144//! let mut a = BTreeMap::new();
145//! a.insert(1, "one");
146//! a.insert(2, "two");
147//! a.insert(3, "three");
148//!
149//! let mut b = BTreeMap::new();
150//! b.insert(2, "two");
151//! b.insert(3, "THREE");
152//! b.insert(4, "four");
153//!
154//! let diff: BTreeMapDiff<'_, i32, &str> = a.diff(&b);
155//!
156//! // Added and removed entries are stored as maps:
157//! assert_eq!(diff.added, [(&4, &"four")].into_iter().collect());
158//! assert_eq!(diff.removed, [(&1, &"one")].into_iter().collect());
159//!
160//! // Common entries are stored as leaf nodes.
161//! assert_eq!(
162//!     diff.common,
163//!     [
164//!         (&2, Leaf { before: &"two", after: &"two" }),
165//!         (&3, Leaf { before: &"three", after: &"THREE" })
166//!     ]
167//!     .into_iter().collect(),
168//! );
169//!
170//! // If `V` implements `Eq`, unchanged and modified iterators become
171//! // available. `unchanged` and `modified` return key-value pairs;
172//! // `unchanged_keys` and `modified_keys` return keys; and
173//! // `unchanged_values` and `modified_values` return values.
174//! //
175//! // Here's `unchanged_keys` to get the keys of unchanged entries:
176//! assert_eq!(diff.unchanged_keys().collect::<Vec<_>>(), [&2]);
177//!
178//! // `modified_values` returns leaf nodes for modified entries.
179//! assert_eq!(
180//!     diff.modified_values().collect::<Vec<_>>(),
181//!     [Leaf { before: &"three", after: &"THREE" }],
182//! );
183//! # }
184//! ```
185//!
186//! ### Set diffs
187//!
188//! For [`BTreeSet`] and [`HashSet`], daft has corresponding [`BTreeSetDiff`]
189//! and [`HashSetDiff`] types. These types have fields for *common*, *added*,
190//! and *removed* entries.
191//!
192//! Set diffs are performed eagerly.
193//!
194//! #### Example
195//!
196//! ```rust
197//! # #[cfg(feature = "std")] {
198//! use daft::{Diffable, Leaf, BTreeSetDiff};
199//! use std::collections::BTreeSet;
200//!
201//! let a: BTreeSet<i32> = [0, 1, 2, 3, 4, 5].into_iter().collect();
202//! let b: BTreeSet<i32> = [3, 4, 5, 6, 7, 8].into_iter().collect();
203//! let diff: BTreeSetDiff<'_, i32> = a.diff(&b);
204//!
205//! assert_eq!(diff.common, [&3, &4, &5].into_iter().collect());
206//! assert_eq!(diff.added, [&6, &7, &8].into_iter().collect());
207//! assert_eq!(diff.removed, [&0, &1, &2].into_iter().collect());
208//! # }
209//! ```
210//!
211//! ### Tuple diffs
212//!
213//! For a tuple like `(A, B, C)`, the [`Diffable`] implementation is recursive:
214//! the diff resolves to `(A::Diff, B::Diff, C::Diff)`.
215//!
216//! #### Example
217//!
218//! ```rust
219//! # #[cfg(feature = "std")] {
220//! use daft::{BTreeSetDiff, Diffable, Leaf};
221//! use std::collections::BTreeSet;
222//!
223//! let before: (usize, String, BTreeSet<usize>) = (1, "hello".to_owned(), [1, 2, 3].into_iter().collect());
224//! let after = (2, "world".to_owned(), [2, 3, 4].into_iter().collect());
225//!
226//! let diff = before.diff(&after);
227//! assert_eq!(
228//!     diff,
229//!     (
230//!         Leaf { before: &1, after: &2 },
231//!         Leaf { before: "hello", after: "world" },
232//!         BTreeSetDiff {
233//!             common: [&2, &3].into_iter().collect(),
234//!             added: [&4].into_iter().collect(),
235//!             removed: [&1].into_iter().collect(),
236//!         }
237//!     ),
238//! );
239//! # }
240//! ```
241//!
242//! ### Struct diffs
243//!
244//! For structs, the [`Diffable`][macro@Diffable] derive macro generates
245//! a diff type with a field corresponding to each field type. Each field must
246//! implement [`Diffable`].
247//!
248//! A struct `Foo` gets a corresponding `FooDiff` struct, which has fields
249//! corresponding to each field in `Foo`.
250//!
251//! #### Struct options
252//!
253//! * `#[daft(leaf)]`: if a **struct** is annotated with this, the [`Diffable`]
254//!   implementation for the struct will be a [`Leaf`] instead of a recursive
255//!   diff.
256//!
257//! #### Field options
258//!
259//! * `#[daft(leaf)]`: if a  **struct field** is annotated with this, the generated
260//!   struct's corresponding field will be a [`Leaf`], regardless of the field's
261//!   `Diff` type (or even whether it implements [`Diffable`] at all).
262//! * `#[daft(ignore)]`: the generated struct's corresponding field is not included
263//!   in the diff.
264//!
265//! #### Example
266//!
267//! For an example of structs with named fields, see [*Usage*](#usage) above.
268//!
269//! Tuple-like structs produce tuple-like diff structs:
270//!
271//! ```rust
272//! # #[cfg(all(feature = "std", feature = "derive"))] {
273//! use daft::Diffable;
274//! use std::collections::BTreeMap;
275//!
276//! #[derive(Diffable)]
277//! struct MyTuple(BTreeMap<i32, &'static str>, i32);
278//!
279//! let before = MyTuple(BTreeMap::new(), 1);
280//! let after = MyTuple([(1, "hello")].into_iter().collect(), 2);
281//! let diff = before.diff(&after);
282//!
283//! // The generated type is MyTupleDiff(BTreeMapDiff<i32, &str>, Leaf<i32>).
284//! assert_eq!(**diff.0.added.get(&1).unwrap(), "hello");
285//! assert_eq!(*diff.1.before, 1);
286//! assert_eq!(*diff.1.after, 2);
287//! # }
288//! ```
289//!
290//! An example with `#[daft(leaf)]` on **structs**:
291//!
292//! ```rust
293//! # #[cfg(feature = "derive")] {
294//! use daft::{Diffable, Leaf};
295//!
296//! #[derive(Diffable)]
297//! #[daft(leaf)]
298//! struct MyStruct {
299//!     a: i32,
300//! }
301//!
302//! let before = MyStruct { a: 1 };
303//! let after = MyStruct { a: 2 };
304//! let diff: Leaf<&MyStruct> = before.diff(&after);
305//!
306//! assert_eq!(diff.before.a, 1);
307//! assert_eq!(diff.after.a, 2);
308//! # }
309//! ```
310//!
311//! An example with `#[daft(leaf)]` on **struct fields**:
312//!
313//! ```rust
314//! # #[cfg(feature = "derive")] {
315//! use daft::{Diffable, Leaf};
316//!
317//! // A simple struct that implements Diffable.
318//! #[derive(Debug, PartialEq, Eq, Diffable)]
319//! struct InnerStruct {
320//!     text: &'static str,
321//! }
322//!
323//! // A struct that does not implement Diffable.
324//! #[derive(Debug, PartialEq, Eq)]
325//! struct PlainStruct(usize);
326//!
327//! #[derive(Diffable)]
328//! struct OuterStruct {
329//!     // Ordinarily, InnerStruct would be diffed recursively, but
330//!     // with #[daft(leaf)], it is treated as a leaf node.
331//!     #[daft(leaf)]
332//!     inner: InnerStruct,
333//!
334//!     // PlainStruct does not implement Diffable, but using
335//!     // daft(leaf) allows it to be diffed anyway.
336//!     #[daft(leaf)]
337//!     plain: PlainStruct,
338//! }
339//!
340//! let before = OuterStruct { inner: InnerStruct { text: "hello" }, plain: PlainStruct(1) };
341//! let after = OuterStruct { inner: InnerStruct { text: "world" }, plain: PlainStruct(2) };
342//! let diff = before.diff(&after);
343//!
344//! // `OuterStructDiff` does *not* recursively diff `InnerStruct`, but instead
345//! // returns a leaf node.
346//! assert_eq!(
347//!     diff.inner,
348//!     Leaf { before: &InnerStruct { text: "hello" }, after: &InnerStruct { text: "world" } },
349//! );
350//!
351//! // But you can continue the recursion anyway, since `InnerStruct` implements
352//! // `Diffable`:
353//! let inner_diff = diff.inner.diff_pair();
354//! assert_eq!(
355//!     inner_diff,
356//!     InnerStructDiff { text: Leaf { before: "hello", after: "world" } },
357//! );
358//!
359//! // `PlainStruct` can also be compared even though it doesn't implement `Diffable`.
360//! assert_eq!(diff.plain, Leaf { before: &PlainStruct(1), after: &PlainStruct(2) });
361//! # }
362//! ```
363//!
364//! ### Custom diff types
365//!
366//! The [`Diffable`] trait can also be implemented manually for custom behavior.
367//!
368//! In general, most custom implementations will likely use one of the built-in
369//! diff types directly.
370//!
371//! ### Example
372//!
373//! Some structs like identifiers should be treated as leaf nodes. This can be
374//! implemented via `#[daft(leaf)]`, but also manually:
375//!
376//! ```rust
377//! # #[cfg(feature = "std")] {
378//! use daft::{Diffable, Leaf};
379//!
380//! struct Identifier(String);
381//!
382//! impl Diffable for Identifier {
383//!     type Diff<'daft> = Leaf<&'daft Self>;
384//!
385//!     fn diff<'daft>(&'daft self, other: &'daft Self) -> Self::Diff<'daft> {
386//!         Leaf {
387//!             before: self,
388//!             after: other,
389//!         }
390//!     }
391//! }
392//! # }
393//! ```
394//!
395//! ## Type and lifetime parameters
396//!
397//! If a type parameter is specified, the [`Diffable`][macro@Diffable] derive
398//! macro for structs normally requires that the type parameter implement
399//! `Diffable`. This is not required if the field is annotated with
400//! `#[daft(leaf)]`.
401//!
402//! Daft fully supports types with arbitrary lifetimes. Automatically generated
403//! diff structs will have an additional `'daft` lifetime parameter at the
404//! beginning, with the requirement that all other lifetime and type parameters
405//! outlive it.
406//!
407//! ### Example
408//!
409//! ```rust
410//! # #[cfg(feature = "derive")] {
411//! use daft::Diffable;
412//!
413//! #[derive(Diffable)]
414//! struct BorrowedData<'a, 'b, T: Diffable + ?Sized> {
415//!     a: &'a str,
416//!     b: &'b T,
417//!     // TODO: example with daft(leaf)
418//! }
419//!
420//! // This generates a struct that looks like:
421//! # /*
422//! #[automatically_derived]
423//! struct BorrowedDataDiff<'daft, 'a: 'daft, 'b: 'daft, T: ?Sized + 'daft> {
424//!     a: Leaf<'daft, &'a str>,
425//!     b: T::Diff<'daft>,
426//! }
427//! # */
428//! # }
429//! ```
430//!
431//! # Optional features
432//!
433//! * `derive`: Enable the `Diffable` derive macro: **disabled** by default.
434//!
435//! Implementations for standard library types, all **enabled** by default:
436//!
437//! * `alloc`: Enable diffing for types from the [`alloc`] crate.
438//! * `std`: Enable diffing for types from the [`std`] crate.
439//!
440//! (With `default-features = false`, daft is no-std compatible.)
441//!
442//! Implementations for third-party types, all **disabled** by default:
443//!
444//! * `uuid1`: Enable diffing for [`uuid::Uuid`].
445//! * `oxnet01`: Enable diffing for network types from the [`oxnet`] crate.
446//! * `newtype-uuid1`: Enable diffing for [`newtype_uuid::TypedUuid`].
447//!
448//! # Minimum supported Rust version (MSRV)
449//!
450//! The minimum supported Rust version is **1.81.0**. At any time, at least the
451//! last three stable versions of Rust are supported.
452//!
453//! While this crate is a pre-release (0.x.x) it may have its MSRV bumped in a
454//! patch release. Once this crate has reached 1.x, any MSRV bump will be
455//! accompanied with a new minor version.
456//!
457//! # Related work
458//!
459//! [Diffus](https://crates.io/crates/diffus) is the original inspiration for
460//! this crate and a great alternative. Daft diverges from diffus in a few ways:
461//!
462//! * Daft's derive macro does not attempt to diff enums with different variants.
463//!   In practice, we've found that diffing enums across different variants is less
464//!   useful than it first appears.
465//!
466//! * Daft has the notion of a [`Leaf`] type, which represents an atomic unit.
467//!   (For example, the [`Diffable`] implementation for `i32` is a [`Leaf`].)
468//!   [`Leaf`]s are also used for enums, as well as in any other place where lazy
469//!   diffing is desired.
470//!
471//! * Diffus has a `Same` trait, which is like `Eq` except it's also implemented
472//!   for floats. Daft doesn't have the `Same` trait, and its core
473//!   functionality forgoes the need for `Eq` entirely.
474//!
475//!   For a primitive scalar like `f64`, you'll get a `Leaf` struct which you can
476//!   compare with whatever notion of equality you want.
477//!
478//! * Daft uses a [generic associated type (GAT)][GAT] so that the `Diffable`
479//!   trait no longer needs a lifetime parameter. This leads to simpler usage.
480//!   (Diffus was written before GATs were available in stable Rust.)
481//!
482//! * Daft uses fewer types in general. For example, diffus wraps its return values
483//!   in an outer `Edit` type, but daft does not.
484//!
485//! * Daft is no-std-compatible, while diffus requires std.
486//!
487//! [GAT]: https://blog.rust-lang.org/2021/08/03/GATs-stabilization-push.html
488//! [`BTreeMap`]: std::collections::BTreeMap
489//! [`BTreeSet`]: std::collections::BTreeSet
490//! [`HashMap`]: std::collections::HashMap
491//! [`HashSet`]: std::collections::HashSet
492
493#[cfg(feature = "alloc")]
494extern crate alloc;
495
496#[macro_use]
497mod macros;
498
499#[cfg(feature = "alloc")]
500mod alloc_impls;
501mod core_impls;
502mod diffable;
503mod leaf;
504#[cfg(feature = "std")]
505mod std_impls;
506mod third_party;
507
508#[cfg(feature = "alloc")]
509pub use alloc_impls::*;
510/// Derive macro for the [`Diffable`] trait.
511///
512/// The behavior of this macro varies by type:
513///
514/// - For **structs**, this macro generates a corresponding recursive (eager)
515///   diff type by default. A non-recursive (lazy) diff can be generated by
516///   annotating the struct overall with `#[daft(leaf)]`.
517/// - For **enums** and **unions**, this macro generates a non-recursive (lazy)
518///   diff.
519///
520/// For more information, see the [crate-level documentation](crate).
521#[cfg(feature = "derive")]
522pub use daft_derive::Diffable;
523pub use diffable::*;
524pub use leaf::*;
525#[cfg(feature = "std")]
526pub use std_impls::*;