assertables/assert_bag/mod.rs
1//! Assert for comparing bag collections.
2//!
3//! These macros help with comparison of bag parameters, such as comparison of
4//! two arrays or two vectors, where the item order does not matter, and the
5//! item count does matter. These macros convert their inputs into HashMap
6//! iterators. See tutorial below.
7//!
8//! For eq & ne:
9//!
10//! * [`assert_bag_eq!(collection1, collection2)`](macro@crate::assert_bag_eq) ≈ bag a = bag b
11//!
12//! * [`assert_bag_ne!(collection1, collection2)`](macro@crate::assert_bag_ne) ≈ bag a ≠ bag b
13//!
14//! For subbag & superbag:
15//!
16//! * [`assert_bag_subbag(a, b)`](macro@crate::assert_bag_subbag) ≈ bag a ⊆ bag b
17//!
18//! * [`assert_bag_superbag!(collection1, collection2)`](macro@crate::assert_bag_superbag) ≈ bag a ⊇ bag b
19//!
20//!
21//! # Example
22//!
23//! ```rust
24//! use assertables::*;
25//!
26//! let a = [1, 1];
27//! let b = [1, 1];
28//! assert_bag_eq!(&a, &b);
29//! ```
30//!
31//! ## Tutorial
32//!
33//! A **bag** means a collection of elements, without any ordering, and tracking duplicate elements.
34//!
35//! A bag is sometimes written by using mathematical notation, which looks like this:
36//!
37//! ```text
38//! bag = {1, 1, 1, 2, 3}
39//! ```
40//!
41//! Bags are equal when they contain all the same elements and corresponding counts in any order:
42//!
43//! ```text
44//! {1, 1, 1, 2, 3} = {1, 1, 1, 2, 3} (same order)
45//! {1, 1, 1, 2, 3} = {1, 3, 1, 2, 1} (different order)
46//! ```
47//!
48//! Bags are not equal when they contain any different elements or any different corresponding counts:
49//!
50//! ```text
51//! {1, 1, 1, 2, 3} ≠ {1, 1, 2, 3}
52//! {1, 1, 1, 2, 3} ≠ {1, 1, 1, 2, 3, 4}
53//! ```
54//!
55//! To create a bag using Rust, one way is to create an array or vector, then convert it into an iterator by using the method `into_iter`, then convert the elements into a map by using `std::collections::BTreeMap` and tracking each element's count:
56//!
57//! ```rust
58//! # use ::std::collections::BTreeMap;
59//! let array = [1, 1, 1, 2, 3];
60//! let mut bag: BTreeMap<_, usize> = BTreeMap::new();
61//! for x in array.into_iter() {
62//! let n = bag.entry(x).or_insert(0);
63//! *n += 1;
64//! }
65//! ```
66//!
67//! To compare two arrays as bags, one way is to convert each array to a bag, then use `assert_eq!` to compare the bags:
68//!
69//! ```rust
70//! # use ::std::collections::BTreeMap;
71//! let array1 = [1, 1, 1, 2, 3];
72//! let array2 = [1, 3, 1, 2, 1];
73//! let mut bag1: BTreeMap<_, usize> = BTreeMap::new();
74//! for x in array1.into_iter() {
75//! let n = bag1.entry(x).or_insert(0);
76//! *n += 1;
77//! }
78//! let mut bag2: BTreeMap<_, usize> = BTreeMap::new();
79//! for x in array2.into_iter() {
80//! let n = bag2.entry(x).or_insert(0);
81//! *n += 1;
82//! }
83//! assert_eq!(bag1, bag2);
84//! ```
85//!
86//! The `assertables` crate provides macros that do the conversion for you:
87//!
88//! ```rust
89//! # use ::std::collections::BTreeMap;
90//! # use assertables::*;
91//! let array1 = [1, 2, 3];
92//! let array2 = [3, 2, 1];
93//! assert_bag_eq!(array1, array2);
94//! ```
95
96/// Assert bag implementation preparation.
97#[macro_export]
98macro_rules! assert_bag_impl_prep {
99 ($impl_into_iter:expr $(,)?) => {
100 match ($impl_into_iter) {
101 impl_into_iter => {
102 let mut bag: std::collections::BTreeMap<_, usize> =
103 std::collections::BTreeMap::new();
104 for x in impl_into_iter.into_iter() {
105 let n = bag.entry(x).or_insert(0);
106 *n += 1;
107 }
108 bag
109 }
110 }
111 };
112}
113
114pub mod assert_bag_eq;
115pub mod assert_bag_ne;
116pub mod assert_bag_subbag;
117pub mod assert_bag_superbag;