comparable/
lib.rs

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