comparable/lib.rs
1#![allow(clippy::derive_partial_eq_without_eq)]
2#![allow(clippy::too_long_first_doc_paragraph)]
3
4//! The `comparable` crate defines the trait [`Comparable`], along with a derive
5//! macro for auto-generating instances of this trait for most data types.
6//! Primarily the purpose of this trait is to offer a method,
7//! [`Comparable::comparison`], by which two values of any type supporting that
8//! trait can yield a summary of the differences between them.
9//!
10//! Note that unlike other crates that do data differencing (primarily between
11//! scalars and collections), `comparable` has been written primarily with testing
12//! in mind. That is, the purpose of generating such change descriptions is to
13//! enable writing tests that assert the set of expected changes after some
14//! operation between an initial state and the resulting state. This goal also
15//! means that some types, like
16//! [`HashMap`](https://doc.rust-lang.org/std/collections/struct.HashMap.html),
17//! must be differenced after ordering the keys first, so that the set of changes
18//! produced can be made deterministic and thus expressible as a test expectation.
19//!
20//! To these ends, the macro [`assert_changes!`] is also provided, taking two
21//! values of the same type along with an expected "change description" as
22//! returned by `foo.comparison(&bar)`. This function uses the
23//! [`pretty_assertions`](https://crates.io/crates/pretty_assertions) crate under
24//! the hood so that minute differences within deep structures can be easily seen
25//! in the failure output.
26//!
27//! # Quickstart
28//!
29//! If you want to get started quickly with the [`Comparable`] crate to enhance unit
30//! testing, do the following:
31//!
32//! 1. Add the `comparable` crate as a dependency, enabling `features = ["derive"]`.
33//! 2. Derive the `Comparable` trait on as many structs and enums as needed.
34//! 3. Structure your unit tests to follow these three phases:
35//! a. Create the initial state or dataset you intend to test and make a copy
36//! of it.
37//! b. Apply your operations and changes to this state.
38//! c. Use [`assert_changes!`] between the initial state and the resulting state
39//! to assert that whatever happened is exactly what you expected to happen.
40//!
41//! The main benefit of this approach over the usual method of "probing" the
42//! resulting state -- to ensure it changed as you expected it to-- is that it
43//! asserts against the exhaustive set of changes to ensure that no unintended
44//! side-effects occurred beyond what you expected to happen. In this way, it is
45//! both a positive and a negative test: checking for what you expect to see as
46//! well as what you don't expect to see.
47//!
48//! # The Comparable trait
49//!
50//! The [`Comparable`] trait has two associated types and two methods, one pair
51//! corresponding to _value descriptions_ and the other to _value changes_:
52//!
53//! ```rust
54//! pub trait Comparable {
55//! type Desc: std::cmp::PartialEq + std::fmt::Debug;
56//! fn describe(&self) -> Self::Desc;
57//!
58//! type Change: std::cmp::PartialEq + std::fmt::Debug;
59//! fn comparison(&self, other: &Self) -> comparable::Changed<Self::Change>;
60//! }
61//! ```
62//!
63//! ## Descriptions: the [`Comparable::Desc`] associated type
64//!
65//! Value descriptions (the [`Comparable::Desc`] associated type) are needed
66//! because value hierarchies can involve many types. Perhaps some of these types
67//! implement `PartialEq` and `Debug`, but not all. To work around this
68//! limitation, the [`Comparable`] derive macro creates a "mirror" of your data
69//! structure with all the same constructors ands field, but using the
70//! [`Comparable::Desc`] associated type for each of its contained types.
71//!
72//! ```
73//! # use comparable_derive::*;
74//! #[derive(Comparable)]
75//! struct MyStruct {
76//! bar: u32,
77//! baz: u32
78//! }
79//! ```
80//!
81//! This generates a description that mirrors the original type, but using type
82//! descriptions rather than the types themselves:
83//!
84//! ```
85//! struct MyStructDesc {
86//! bar: <u32 as comparable::Comparable>::Desc,
87//! baz: <u32 as comparable::Comparable>::Desc
88//! }
89//! ```
90//!
91//! You may also choose an alternate description type, such as a reduced form of a
92//! value or some other type entirely. For example, complex structures could
93//! describe themselves by the set of changes they represent from a `Default`
94//! value. This is so common, that it's supported via a `compare_default` macro
95//! attribute provided by `comparable`:
96//!
97//! ```
98//! # use comparable_derive::*;
99//! #[derive(Comparable)]
100//! #[compare_default]
101//! struct MyStruct { /* ...lots of fields... */ }
102//!
103//! impl Default for MyStruct {
104//! fn default() -> Self { MyStruct {} }
105//! }
106//! ```
107//!
108//! For scalars, the [`Comparable::Desc`] type is the same as the type it's
109//! describing, and these are called "self-describing".
110//!
111//! There are other macro attributes provided for customizing things even further,
112//! which are covered below, beginning at the section on [Structures](#structs).
113//!
114//! ## Changes: the [`Comparable::Change`] associated type
115//!
116//! When two values of a type differ, this difference gets represented using the
117//! associated type [`Comparable::Change`]. Such values are produced by the
118//! [`Comparable::comparison`] method, which actually returns `Changed<Change>`
119//! since the result may be either `Changed::Unchanged` or
120//! `Changed::Changed(_changes_)`.[^option]
121//!
122//! [^option] `Changed` is just a different flavor of the `Option` type, created
123//! to make changesets clearer than just seeing `Some` in various places.
124//!
125//! The primary purpose of a [`Comparable::Change`] value is to compare it to a
126//! set of changes you expected to see, so design choices have been made to
127//! optimize for clarity and printing rather than, say, the ability to transform
128//! one value into another by applying a changeset. This is entirely possible give
129//! a dataset and a change description, but no work has been done to achieve this
130//! goal.
131//!
132//! How changes are represented can differ greatly between scalars, collections,
133//! structs and enums, so more detail is given below in the section discussing
134//! each of these types.
135//!
136//! # Scalars
137//!
138//! [`Comparable`] traits have been implemented for all of the basic scalar types.
139//! These are self-describing, and use a [`Comparable::Change`] structure named
140//! after the type that holds the previous and changed values. For example, the
141//! following assertions hold:
142//!
143//! ```
144//! # use comparable::*;
145//! assert_changes!(&100, &100, Changed::Unchanged);
146//! assert_changes!(&100, &200, Changed::Changed(I32Change(100, 200)));
147//! assert_changes!(&true, &false, Changed::Changed(BoolChange(true, false)));
148//! assert_changes!(
149//! &"foo",
150//! &"bar",
151//! Changed::Changed(StringChange("foo".to_string(), "bar".to_string())),
152//! );
153//! ```
154//!
155//! # Vec and Set Collections
156//!
157//! The set collections for which [`Comparable`] has been implemented are: `Vec`,
158//! `HashSet`, and `BTreeSet`.
159//!
160//! The `Vec` uses `Vec<VecChange>` to report all of the indices at which changes
161//! happened. Note that it cannot detect insertions in the middle, and so will
162//! likely report every item as changed from there until the end of the vector, at
163//! which point it will report an added member.
164//!
165//! `HashSet` and `BTreeSet` types both report changes the same way, using the
166//! `SetChange` type. Note that in order for `HashSet` change results to be
167//! deterministic, the values in a `HashSet` must support the `Ord` trait so they
168//! can be sorted prior to comparison. Sets cannot tell when specific members have
169//! change, and so only report changes in terms of `SetChange::Added` and
170//! `SetChange::Removed`.
171//!
172//! Here are a few examples, taken from the `comparable_test` test suite:
173//!
174//! ```
175//! # use comparable::*;
176//! # use std::collections::HashSet;
177//! // Vectors
178//! assert_changes!(
179//! &vec![1 as i32, 2],
180//! &vec![1 as i32, 2, 3],
181//! Changed::Changed(vec![VecChange::Added(2, 3)]),
182//! );
183//! assert_changes!(
184//! &vec![1 as i32, 3],
185//! &vec![1 as i32, 2, 3],
186//! Changed::Changed(vec![
187//! VecChange::Changed(1, I32Change(3, 2)),
188//! VecChange::Added(2, 3),
189//! ]),
190//! );
191//! assert_changes!(
192//! &vec![1 as i32, 2, 3],
193//! &vec![1 as i32, 3],
194//! Changed::Changed(vec![
195//! VecChange::Changed(1, I32Change(2, 3)),
196//! VecChange::Removed(2, 3),
197//! ]),
198//! );
199//! assert_changes!(
200//! &vec![1 as i32, 2, 3],
201//! &vec![1 as i32, 4, 3],
202//! Changed::Changed(vec![VecChange::Changed(1, I32Change(2, 4))]),
203//! );
204//!
205//! // Sets
206//! assert_changes!(
207//! &vec![1 as i32, 2].into_iter().collect::<HashSet<_>>(),
208//! &vec![1 as i32, 2, 3].into_iter().collect::<HashSet<_>>(),
209//! Changed::Changed(vec![SetChange::Added(3)]),
210//! );
211//! assert_changes!(
212//! &vec![1 as i32, 3].into_iter().collect::<HashSet<_>>(),
213//! &vec![1 as i32, 2, 3].into_iter().collect::<HashSet<_>>(),
214//! Changed::Changed(vec![SetChange::Added(2)]),
215//! );
216//! assert_changes!(
217//! &vec![1 as i32, 2, 3].into_iter().collect::<HashSet<_>>(),
218//! &vec![1 as i32, 3].into_iter().collect::<HashSet<_>>(),
219//! Changed::Changed(vec![SetChange::Removed(2)]),
220//! );
221//! assert_changes!(
222//! &vec![1 as i32, 2, 3].into_iter().collect::<HashSet<_>>(),
223//! &vec![1 as i32, 4, 3].into_iter().collect::<HashSet<_>>(),
224//! Changed::Changed(vec![SetChange::Added(4), SetChange::Removed(2)]),
225//! );
226//! ```
227//!
228//! Note that if the first `VecChange::Change` above had used an index of 1
229//! instead of 0, the resulting failure would look something like this:
230//!
231//! ```text
232//! running 1 test
233//! test test_comparable_bar ... FAILED
234//!
235//! failures:
236//!
237//! ---- test_comparable_bar stdout ----
238//! thread 'test_comparable_bar' panicked at 'assertion failed: `(left == right)`
239//!
240//! Diff < left / right > :
241//! Changed(
242//! [
243//! Change(
244//! < 1,
245//! > 0,
246//! I32Change(
247//! 100,
248//! 200,
249//! ),
250//! ),
251//! ],
252//! )
253//!
254//! ', /Users/johnw/src/comparable/comparable/src/lib.rs:19:5
255//! note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
256//!
257//!
258//! failures:
259//! test_comparable_bar
260//! ```
261//!
262//! # Map Collections
263//!
264//! The map collections for which [`Comparable`] has been implemented are:
265//! `HashMap`, and `BTreeMap`.
266//!
267//! Both report changes the same way, using the `MapChange` type. Note that in
268//! order for `HashMap` change results to be deterministic, the keys in a
269//! `HashMap` must support the `Ord` trait so they can be sorted prior to
270//! comparison. Changes are reported in terms of `MapChange::Added`,
271//! `MapChange::Removed` and `MapChange::Changed`, exactly like `VecChange` above.
272//!
273//! Here are a few examples, taken from the `comparable_test` test suite:
274//!
275//! ```
276//! # use comparable::*;
277//! # use std::collections::HashMap;
278//! // HashMaps
279//! assert_changes!(
280//! &vec![(0, 1 as i32), (1, 2)].into_iter().collect::<HashMap<_, _>>(),
281//! &vec![(0, 1 as i32), (1, 2), (2, 3)].into_iter().collect::<HashMap<_, _>>(),
282//! Changed::Changed(vec![MapChange::Added(2, 3)]),
283//! );
284//! assert_changes!(
285//! &vec![(0, 1 as i32), (1, 2), (2, 3)].into_iter().collect::<HashMap<_, _>>(),
286//! &vec![(0, 1 as i32), (1, 2)].into_iter().collect::<HashMap<_, _>>(),
287//! Changed::Changed(vec![MapChange::Removed(2)]),
288//! );
289//! assert_changes!(
290//! &vec![(0, 1 as i32), (2, 3)].into_iter().collect::<HashMap<_, _>>(),
291//! &vec![(0, 1 as i32), (1, 2), (2, 3)].into_iter().collect::<HashMap<_, _>>(),
292//! Changed::Changed(vec![MapChange::Added(1, 2)]),
293//! );
294//! assert_changes!(
295//! &vec![(0, 1 as i32), (1, 2), (2, 3)].into_iter().collect::<HashMap<_, _>>(),
296//! &vec![(0, 1 as i32), (2, 3)].into_iter().collect::<HashMap<_, _>>(),
297//! Changed::Changed(vec![MapChange::Removed(1)]),
298//! );
299//! assert_changes!(
300//! &vec![(0, 1 as i32), (1, 2), (2, 3)].into_iter().collect::<HashMap<_, _>>(),
301//! &vec![(0, 1 as i32), (1, 4), (2, 3)].into_iter().collect::<HashMap<_, _>>(),
302//! Changed::Changed(vec![MapChange::Changed(1, I32Change(2, 4))]),
303//! );
304//! ```
305//!
306//! # <a name="structs"></a>Structures
307//!
308//! Differencing arbitrary structures was the original motive for creating
309//! `comparable`. This is made feasible using a [`Comparable`] derive macro that
310//! auto-generates code needed for such comparisons. The purpose of this section
311//! is to explain how this macro works, and the various attribute macros that can
312//! be used to guide the process. If all else fails, manual trait implementations
313//! are always an alternative.
314//!
315//! Here is what deriving `Change` for a structure with multiple fields typically
316//! produces:
317//!
318//! ```
319//! # use comparable_derive::*;
320//! # use comparable::*;
321//! struct MyStruct {
322//! bar: u32,
323//! baz: u32,
324//! }
325//!
326//! // The following would be generated by `#[derive(Comparable)]`:
327//!
328//! #[derive(PartialEq, Debug)]
329//! struct MyStructDesc {
330//! bar: <u32 as Comparable>::Desc,
331//! baz: <u32 as Comparable>::Desc,
332//! }
333//!
334//! #[derive(PartialEq, Debug)]
335//! enum MyStructChange {
336//! Bar(<u32 as Comparable>::Change),
337//! Baz(<u32 as Comparable>::Change),
338//! }
339//!
340//! impl Comparable for MyStruct {
341//! type Desc = MyStructDesc;
342//!
343//! fn describe(&self) -> Self::Desc {
344//! MyStructDesc {
345//! bar: self.bar.describe(),
346//! baz: self.baz.describe(),
347//! }
348//! }
349//!
350//! type Change = Vec<MyStructChange>;
351//!
352//! fn comparison(&self, other: &Self) -> Changed<Self::Change> {
353//! let changes: Self::Change = vec![
354//! self.bar.comparison(&other.bar).map(MyStructChange::Bar),
355//! self.baz.comparison(&other.baz).map(MyStructChange::Baz),
356//! ]
357//! .into_iter()
358//! .flatten()
359//! .collect();
360//! if changes.is_empty() {
361//! Changed::Unchanged
362//! } else {
363//! Changed::Changed(changes)
364//! }
365//! }
366//! }
367//! ```
368//!
369//! For structs with one field or no fields, see the related section below.
370//!
371//! ## Field attribute: `comparable_ignore`
372//!
373//! The first attribute macro you'll notice that can be applied to individual
374//! fields is `#[comparable_ignore]`, which must be used if the type in question
375//! cannot be compared for differences.
376//!
377//! ## Field attribute: `comparable_synthetic`
378//!
379//! The `#[comparable_synthetic { <BINDINGS...> }]` attribute allows you to attach
380//! one or more "synthetic properties" to a field, which are then considered in
381//! both descriptions and change sets, as if they were actual fields with the
382//! computed value. Here is an example:
383//!
384//! ```
385//! # use comparable_derive::*;
386//! #[derive(Comparable)]
387//! pub struct Synthetics {
388//! #[comparable_synthetic {
389//! let full_value = |x: &Self| -> u8 { x.ensemble.iter().sum() };
390//! }]
391//! #[comparable_ignore]
392//! pub ensemble: Vec<u8>,
393//! }
394//! ```
395//!
396//! This structure has an `ensemble` field containing a vector of `u8` values.
397//! However, in tests we may not care if the vector's contents change, so long as
398//! the final sum remains the same. This is done by ignoring the ensemble field so
399//! that it's not generated or described at all, while creating a synthetic field
400//! _derived from the full object_ that yields the sum.
401//!
402//! Note that the syntax for the `comparable_synthetic` attribute is rather
403//! specific: a series of simply-named `let` bindings, where the value in each
404//! case is a fully typed closure that takes a reference to the object containing
405//! the original field (`&Self`), and yields a value of some type for which
406//! [`Comparable`] has been implemented or derived.
407//!
408//! ## Deriving `Comparable` for structs: the `Desc` type
409//!
410//! By default, deriving [`Comparable`] for a structure will create a "mirror" of
411//! that structure, with all the same fields, but replacing every type `T` with
412//! `<T as Comparable>::Desc`:
413//!
414//! ```
415//! # use comparable::*;
416//! struct MyStructDesc {
417//! bar: <u32 as Comparable>::Desc,
418//! baz: <u32 as Comparable>::Desc
419//! }
420//! ```
421//!
422//! This process can be influenced using several attribute macros.
423//!
424//! ### Macro attribute: `self_describing`
425//!
426//! If the `self_describing` attribute is used, the [`Comparable::Desc`] type is
427//! set to be the type itself, and the [`Comparable::describe`] method return a
428//! clone of the value.
429//!
430//! Note the following traits are required for self-describing types: `Clone`,
431//! `Debug` and `PartialEq`.
432//!
433//! ### Macro attribute: `no_description`
434//!
435//! If you want no description at all for a type, since you only care about how it
436//! has changed and never want to report a description of the value in any other
437//! context, then you can use `#[no_description]`. This sets the
438//! [`Comparable::Desc`] type to be unit, and the [`Comparable::describe`] method
439//! accordingly:
440//!
441//! ```ignore
442//! type Desc = ();
443//!
444//! fn describe(&self) -> Self::Desc {
445//! ()
446//! }
447//! ```
448//!
449//! It is assumed that when this is appropriate, such values will never appear in
450//! any change output, so consider a different approach if you see lots of units
451//! turning up.
452//!
453//! ### Macro attribute: `describe_type` and `describe_body`
454//!
455//! You can have more control over description by specifying exactly the text that
456//! should appear for the [`Comparable::Desc`] type and the body of the
457//! [`Comparable::describe`] function. Basically, for the following definition:
458//!
459//! ```ignore
460//! # use comparable_derive::*;
461//! #[derive(Comparable)]
462//! #[describe_type(T)]
463//! #[describe_body(B)]
464//! struct MyStruct {
465//! bar: u32,
466//! baz: u32
467//! }
468//! ```
469//!
470//! The following is generated:
471//!
472//! ```ignore
473//! type Desc = T;
474//!
475//! fn describe(&self) -> Self::Desc {
476//! B
477//! }
478//! ```
479//!
480//! This also means that the expression argument passed to `describe_body` may
481//! reference the `self` parameter. Here is a real-world use case:
482//!
483//! ```
484//! # use comparable_derive::*;
485//! #[cfg_attr(feature = "comparable",
486//! derive(comparable::Comparable),
487//! describe_type(String),
488//! describe_body(self.to_string()))]
489//! struct MyStruct {}
490//! ```
491//!
492//! This same approach could be used to represent large blobs of data by their
493//! checksum hash, for example, or large data structures that you don't need to
494//! ever display by their Merkle root hash.
495//!
496//! ### Macro attribute: `compare_default`
497//!
498//! When the `#[compare_default]` attribute macro is used, the
499//! [`Comparable::Desc`] type is defined to be the same as the
500//! [`Comparable::Change`] type, with the [`Comparable::describe`] method being
501//! implemented as a comparison against the value of `Default::default()`:
502//!
503//! ```ignore
504//! # use comparable::*;
505//! impl comparable::Comparable for MyStruct {
506//! type Desc = Self::Change;
507//!
508//! fn describe(&self) -> Self::Desc {
509//! MyStruct::default().comparison(self).unwrap_or_default()
510//! }
511//!
512//! type Change = Vec<MyStructChange>;
513//!
514//! /* ... */
515//! }
516//! ```
517//!
518//! Note that changes for structures are always a vector, since this allows
519//! changes to be reported separately for each field. More on this in the
520//! following section.
521//!
522//! ## Macro attribute: `comparable_public` and `comparable_private`
523//!
524//! By default, the auto-generated [`Comparable::Desc`] and [`Comparable::Change`]
525//! types have the same visibility as their parent. This may not be appropriate,
526//! however, if you want to keep the original data type private but allow
527//! exporting of descriptions and change sets. To support this -- and the converse
528//! -- you can use `#[comparable_public]` and `#[comparable_private]` to be
529//! explicit about the visibility of these generated types.
530//!
531//! ### Special case: Unit structs
532//!
533//! If a struct has no fields it can never change, and so only a unitary
534//! [`Comparable::Desc`] type is generated.
535//!
536//! ### Special case: Singleton structs
537//!
538//! If a struct has only one fields, whether named or unnamed, it no longer makes
539//! sense to use a vector of enum values to record what has changed. In this case
540//! the derivation becomes much simpler:
541//!
542//! ```
543//! # use comparable_derive::*;
544//! # use comparable::*;
545//! struct MyStruct {
546//! bar: u32,
547//! }
548//!
549//! // The following would be generated by `#[derive(Comparable)]`:
550//!
551//! #[derive(PartialEq, Debug)]
552//! struct MyStructDesc {
553//! bar: <u32 as Comparable>::Desc,
554//! }
555//!
556//! #[derive(PartialEq, Debug)]
557//! struct MyStructChange {
558//! bar: <u32 as Comparable>::Change,
559//! }
560//!
561//! impl Comparable for MyStruct {
562//! type Desc = MyStructDesc;
563//!
564//! fn describe(&self) -> Self::Desc {
565//! MyStructDesc { bar: self.bar.describe() }
566//! }
567//!
568//! type Change = MyStructChange;
569//!
570//! fn comparison(&self, other: &Self) -> Changed<Self::Change> {
571//! self.bar.comparison(&other.bar).map(|x| MyStructChange { bar: x })
572//! }
573//! }
574//! ```
575//!
576//! ## Deriving `Comparable` for structs: the `Change` type
577//!
578//! By default for structs, deriving [`Comparable`] creates an `enum` with
579//! variants for each field in the `struct`, and it represents changes using a
580//! vector of such values. This means that for the following definition:
581//!
582//! ```
583//! # use comparable_derive::*;
584//! #[derive(Comparable)]
585//! struct MyStruct {
586//! bar: u32,
587//! baz: u32
588//! }
589//! ```
590//!
591//! The [`Comparable::Change`] type is defined to be `Vec<MyStructChange>`, with
592//! `MyStructChange` as follows:
593//!
594//! ```ignore
595//! #[derive(PartialEq, Debug)]
596//! enum MyStructChange {
597//! Bar(<u32 as Comparable>::Change),
598//! Baz(<u32 as Comparable>::Change),
599//! }
600//!
601//! impl comparable::Comparable for MyStruct {
602//! type Desc = MyStructDesc;
603//! type Change = Vec<MyStructChange>;
604//! }
605//! ```
606//!
607//! Note that if a struct has only one field, there is no reason to specify
608//! changes using a vector, since either the struct is unchanged or just that one
609//! field has changed. For this reason, singleton structs optimize away the vector
610//! and use `type Change = [type]Change` in their [`Comparable`] derivation,
611//! rather than `type Change = Vec<[type]Change>` as for multi-field structs.
612//!
613//! Here is an abbreviated example of how this looks when asserting changes for a
614//! struct with multiple fields:
615//!
616//! ```ignore
617//! assert_changes!(
618//! &initial_foo, &later_foo,
619//! Changed::Changed(vec![
620//! MyStructChange::Bar(...),
621//! MyStructChange::Baz(...),
622//! ]));
623//! ```
624//!
625//! If the field hasn't been changed it won't appear in the vector, and each field
626//! appears at most once. The reason for taking this approach is that structures
627//! with many, many fields can be represented by a small change set if most of the
628//! other fields were left untouched.
629//!
630//! # <a name="enums"></a>Enumerations
631//!
632//! Enumerations are handled quite differently from structures, for the reason
633//! that while a `struct` is always a product of fields, an `enum` can be more
634//! than a sum of variants -- but also a sum of products.
635//!
636//! To unpack that a bit: By "a product of fields", this means that a `struct` is
637//! a simple grouping of typed fields, where the same fields are available for
638//! _every_ value of such a structure.
639//!
640//! Meanwhile an `enum` is a sum, or choice, among variants. However, some of
641//! these variants can themselves contain groups of fields, as though there were
642//! an unnamed structure embedded in the variant. Consider the following `enum`:
643//!
644//! ```
645//! # use comparable_derive::*;
646//! #[derive(Comparable)]
647//! enum MyEnum {
648//! One(bool),
649//! Two { two: Vec<bool>, two_more: u32 },
650//! Three,
651//! }
652//! ```
653//!
654//! Here we see variant that has a variant with no fields (`Three`), one with
655//! unnamed fields (`One`), and one with named fields like a usual structure
656//! (`Two`). The problem, though, is that these embedded structures are never
657//! represented as independent types, so we can't define [`Comparable`] for them
658//! and just compute the differences between the enum arguments. Nor can we just
659//! create a copy of the field type with a real name and generate [`Comparable`]
660//! for it, because not every value is copyable or clonable, and it gets very
661//! tricky to auto-generate a new hierarchy built out fields with reference types
662//! all the way down...
663//!
664//! Instead, the following gets generated, which can end up being a bit verbose,
665//! but captures the full nature of any differences:
666//!
667//! ```ignore
668//! enum MyEnumChange {
669//! BothOne(<bool as comparable::Comparable>::Change),
670//! BothTwo {
671//! two: Changed<<Vec<bool> as comparable::Comparable>::Change>,
672//! two_more: Changed<Baz as comparable::Comparable>::Change
673//! },
674//! BothThree,
675//! Different(
676//! <MyEnum as comparable::Comparable>::Desc,
677//! <MyEnum as comparable::Comparable>::Desc
678//! ),
679//! }
680//! ```
681//!
682//! Note that variants with singleton fields do not use [`Comparable::Change`],
683//! since that information is already reflected when the variant is reported as
684//! having changed at all using, for example, `BothOne`. In the case of `BothTwo`,
685//! each of the field types is wrapped in `Changed` because it's possible that
686//! either one or both of the fields may changed.
687//!
688//! Below is a full example of what gets derived for the enum above:
689//!
690//! ```
691//! # use comparable_derive::*;
692//! # use comparable::*;
693//! enum MyEnum {
694//! One(bool),
695//! Two { two: Vec<bool>, two_more: u32 },
696//! Three,
697//! }
698//!
699//! // The following would be generated by `#[derive(Comparable)]`:
700//!
701//! #[derive(PartialEq, Debug)]
702//! enum MyEnumDesc {
703//! One(<bool as Comparable>::Desc),
704//! Two { two: <Vec<bool> as Comparable>::Desc,
705//! two_more: <u32 as Comparable>::Desc },
706//! Three,
707//! }
708//!
709//! #[derive(PartialEq, Debug)]
710//! enum MyEnumChange {
711//! BothOne(<bool as Comparable>::Change),
712//! BothTwo { two: Changed<<Vec<bool> as Comparable>::Change>,
713//! two_more: Changed<<u32 as Comparable>::Change> },
714//! BothThree,
715//! Different(MyEnumDesc, MyEnumDesc),
716//! }
717//!
718//! impl Comparable for MyEnum {
719//! type Desc = MyEnumDesc;
720//!
721//! fn describe(&self) -> Self::Desc {
722//! match self {
723//! MyEnum::One(x) => MyEnumDesc::One(x.describe()),
724//! MyEnum::Two { two: x, two_more: y } =>
725//! MyEnumDesc::Two { two: x.describe(),
726//! two_more: y.describe() },
727//! MyEnum::Three => MyEnumDesc::Three,
728//! }
729//! }
730//!
731//! type Change = MyEnumChange;
732//!
733//! fn comparison(&self, other: &Self) -> Changed<Self::Change> {
734//! match (self, other) {
735//! (MyEnum::One(x), MyEnum::One(y)) =>
736//! x.comparison(&y).map(MyEnumChange::BothOne),
737//! (MyEnum::Two { two: x0, two_more: x1 },
738//! MyEnum::Two { two: y0, two_more: y1 }) => {
739//! let c0 = x0.comparison(&y0);
740//! let c1 = x1.comparison(&y1);
741//! if c0.is_unchanged() && c1.is_unchanged() {
742//! Changed::Unchanged
743//! } else {
744//! Changed::Changed(MyEnumChange::BothTwo {
745//! two: c0, two_more: c1
746//! })
747//! }
748//! }
749//! (MyEnum::Three, MyEnum::Three) => Changed::Unchanged,
750//! (_, _) => Changed::Changed(
751//! MyEnumChange::Different(self.describe(), other.describe()))
752//! }
753//! }
754//! }
755//! ```
756//!
757//! ## Deriving `Comparable` for enums: the `Desc` type
758//!
759//! By default for enums, deriving [`Comparable`] creates a "mirror" of that
760//! structure, with all the same variants and fields, but replacing every type `T`
761//! with `<T as Comparable>::Desc`:
762//!
763//! ```
764//! # use comparable::*;
765//! enum MyEnumDesc {
766//! Bar(<u32 as Comparable>::Desc),
767//! Baz { some_field: <u32 as Comparable>::Desc }
768//! }
769//! ```
770//!
771//! This process can be influenced using the same attribute macros as for structs,
772//! with the exception that synthetic properties are not yet supported on fields
773//! of enum variants. Use of this attribute in that context is silently ignored at
774//! present.
775//!
776//! **TODO**: jww (2021-11-01): Allow for synthetic fields in enum variants.
777//!
778//! ## Deriving `Comparable` for enums: the `Change` type
779//!
780//! By default for enums, deriving [`Comparable`] create a related `enum` where
781//! each variant from the original is represented by a `Both<Name>` variant in the
782//! `Change` type, and a new variant named `Different` is added that takes two
783//! description of the original enum.
784//!
785//! Whenever two enum values are compared and they have different variants, the
786//! `Different` variant of the `Change` type is used to represent a description of
787//! the differing values. If the values share the same variant, then
788//! `Both<Variant>` is used.
789//!
790//! Note that `Both<Variant>` has two forms: For variant with a single named or
791//! unnamed field, it is simply the `Change` type associated with the original
792//! field type; for variants with multiple named or unnamed fields, each `Change`
793//! type is also wrapped in a `Changed` structure, to reflect whether that field
794//! of the variant changed or not.
795//!
796//! ## Field attribute: `variant_struct_fields`
797//!
798//! Note that it is possible to treat variant fields as though they were structs,
799//! and then to compare them exactly the same way as for structs above. This is
800//! not the default because enum variants with named fields typically contain
801//! fewer fields on average than structs, and it would increase verbosity in the
802//! change description to always have to name these implied structs. However, in
803//! cases where the number of fields found in variants is large, it can be just as
804//! benifical as for structs.
805//!
806//! For this reason, the macro attribute `variant_struct_fields` is provided to
807//! derive such transformations. For example, it would cause the following code to
808//! be generated, with the main difference between the new `MyEnumTwoChange` type
809//! and how it is used:
810//!
811//! ```
812//! # use comparable_derive::*;
813//! # use comparable::*;
814//! enum MyEnum {
815//! One(bool),
816//! Two { two: Vec<bool>, two_more: u32 },
817//! Three,
818//! }
819//!
820//! // The following would be generated by `#[derive(Comparable)]`:
821//!
822//! #[derive(PartialEq, Debug)]
823//! enum MyEnumDesc {
824//! One(<bool as Comparable>::Desc),
825//! Two { two: <Vec<bool> as Comparable>::Desc,
826//! two_more: <u32 as Comparable>::Desc },
827//! Three,
828//! }
829//!
830//! #[derive(PartialEq, Debug)]
831//! enum MyEnumChange {
832//! BothOne(<bool as Comparable>::Change),
833//! BothTwo(Vec<MyEnumTwoChange>),
834//! BothThree,
835//! Different(MyEnumDesc, MyEnumDesc),
836//! }
837//!
838//! #[derive(PartialEq, Debug)]
839//! enum MyEnumTwoChange {
840//! Two(<Vec<bool> as Comparable>::Change),
841//! TwoMore(<u32 as Comparable>::Change),
842//! }
843//!
844//! impl Comparable for MyEnum {
845//! type Desc = MyEnumDesc;
846//!
847//! fn describe(&self) -> Self::Desc {
848//! match self {
849//! MyEnum::One(x) => MyEnumDesc::One(x.describe()),
850//! MyEnum::Two { two: x, two_more: y } =>
851//! MyEnumDesc::Two { two: x.describe(),
852//! two_more: y.describe() },
853//! MyEnum::Three => MyEnumDesc::Three,
854//! }
855//! }
856//!
857//! type Change = MyEnumChange;
858//!
859//! fn comparison(&self, other: &Self) -> Changed<Self::Change> {
860//! match (self, other) {
861//! (MyEnum::One(x), MyEnum::One(y)) =>
862//! x.comparison(&y).map(MyEnumChange::BothOne),
863//! (MyEnum::Two { two: x0, two_more: x1 },
864//! MyEnum::Two { two: y0, two_more: y1 }) => {
865//! let c0 = x0.comparison(&y0);
866//! let c1 = x1.comparison(&y1);
867//! let changes: Vec<MyEnumTwoChange> = vec![
868//! c0.map(MyEnumTwoChange::Two),
869//! c1.map(MyEnumTwoChange::TwoMore),
870//! ].into_iter().flatten().collect();
871//! if changes.is_empty() {
872//! Changed::Unchanged
873//! } else {
874//! Changed::Changed(MyEnumChange::BothTwo(changes))
875//! }
876//! }
877//! (MyEnum::Three, MyEnum::Three) => Changed::Unchanged,
878//! (_, _) => Changed::Changed(
879//! MyEnumChange::Different(self.describe(), other.describe()))
880//! }
881//! }
882//! }
883//! ```
884//!
885//! ## Special case: Empty enums
886//!
887//! If a enum has no variants it cannot be constructed, so both the
888//! [`Comparable::Desc`] or [`Comparable::Change`] types are omitted and it is
889//! always reported as unchanged.
890//!
891//! # <a name="unions"></a>Unions
892//!
893//! Unions cannot derive [`Comparable`] instances at the present time.
894#[doc(hidden)]
895pub mod array;
896#[doc(hidden)]
897pub mod boxes;
898#[doc(hidden)]
899pub mod empty;
900#[doc(hidden)]
901pub mod map;
902#[doc(hidden)]
903pub mod opt;
904#[doc(hidden)]
905pub mod path;
906#[doc(hidden)]
907pub mod scalar;
908#[doc(hidden)]
909pub mod set;
910#[doc(hidden)]
911pub mod string;
912#[doc(hidden)]
913pub mod tuple;
914#[doc(hidden)]
915pub mod types;
916
917#[doc(hidden)]
918pub use crate::map::*;
919#[doc(hidden)]
920pub use crate::opt::*;
921#[doc(hidden)]
922pub use crate::path::*;
923#[doc(hidden)]
924pub use crate::scalar::*;
925#[doc(hidden)]
926pub use crate::set::*;
927#[doc(hidden)]
928pub use crate::string::*;
929
930pub use crate::types::{Changed, Comparable};
931
932/// Assert that two values of a type have changed in the way described.
933#[macro_export]
934macro_rules! assert_changes {
935 ($left:expr, $right:expr, $expected:expr$(,)?) => {{
936 use comparable::Comparable;
937 assert_eq!($expected, $left.comparison(&$right));
938 }};
939}
940
941/// Assert that two values of a type have either not changed, or they have
942/// changed only to the extent described by the give change set. This allows
943/// tests to confirm that what they expected to see happened, and anything
944/// they didn't expect to see in fact did not happen.
945///
946/// This function is just a wrapper around `pretty_assertions::assert_eq!`
947/// and does the following:
948/// ```ignore
949/// pretty_assertions::assert_eq!(expected, left.comparison(right))
950/// ```
951#[macro_export]
952macro_rules! pretty_assert_changes {
953 ($left:expr, $right:expr, $expected:expr$(,)?) => {{
954 use comparable::Comparable;
955 pretty_assertions::assert_eq!($expected, $left.comparison(&$right));
956 }};
957}
958
959/// Assert that two values of a type have changed in the way described,
960/// stopping at the first failure if this occurs in a proptest block.
961#[macro_export]
962macro_rules! prop_assert_changes {
963 ($left:expr, $right:expr, $expected:expr$(,)?) => {{
964 use comparable::Comparable;
965 proptest::prop_assert_eq!($expected, $left.comparison(&$right));
966 }};
967}
968
969/// Assert that two values of a type have changed in the way described,
970/// stopping at the first failure if this occurs in a proptest block.
971#[macro_export]
972macro_rules! prop_pretty_assert_changes {
973 ($left:expr, $right:expr, $expected:expr$(,)?) => {{
974 use comparable::Comparable;
975 let changes = $left.comparison(&$right);
976 if $expected != changes {
977 return Err(proptest::test_runner::TestCaseError::fail(format!(
978 "prop_assert_changes! failed: `(left == right)`\
979 \n\
980 \n{}\
981 \n",
982 pretty_assertions::Comparison::new(&$expected, &changes)
983 )));
984 }
985 }};
986}
987
988// Re-export #[derive(Comparable)].
989//
990// The reason re-exporting is not enabled by default is that disabling it would
991// be annoying for crates that provide handwritten impls or data formats. They
992// would need to disable default features and then explicitly re-enable std.
993#[cfg(feature = "comparable_derive")]
994#[doc(hidden)]
995pub use comparable_derive::*;