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::*;