iter_diff/
lib.rs

1//! Differences between iterators
2//!
3//! The [`IterDiff`] trait can be used to iterate through the differences
4//! between two iterators. The differences between each element are enumerated
5//! by [`Diff`]. The variants of the enum express the changes one would need to
6//! make to the original iterator in order to attain the second.
7//!
8//! ```
9//! use iter_diff::prelude::*;
10//!
11//! let a = [0, 1, 2, 3];
12//! let b = [0, 2, 2];
13//!
14//! let diffs: Vec<_> = a.iter_diff(b).collect();
15//! assert_eq!(diffs.len(), 4);
16//!
17//! assert_eq!(diffs[0], Diff::Keep);
18//! assert_eq!(diffs[1], Diff::Change(2));
19//! assert_eq!(diffs[2], Diff::Keep);
20//! assert_eq!(diffs[3], Diff::Remove);
21//! ```
22
23#![deny(missing_docs)]
24#![deny(clippy::all)]
25
26pub mod prelude;
27
28/// The difference between two iterator elements.
29#[derive(Debug, Hash, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
30pub enum Diff<T> {
31    /// Changed item. The element of the left-hand side iterator differs from
32    /// that of the right-hand side iterator. The new element is returned
33    /// contained in this variant.
34    Change(T),
35    /// Removed item. The element of the left-hand side iterator is not present
36    /// in the right-hand side iterator.
37    Remove,
38    /// Kept item. The element of the left-hand side iterator is the same as
39    /// the element of the right-hand side iterator.
40    Keep,
41    /// Added item. The left-hand side iterator does not contain this element
42    /// of the right-hand side iterator.
43    Add(T),
44}
45
46/// An iterator of the differences between of two iterators.
47#[derive(Debug, Hash, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
48pub struct DiffIter<Lhs, Rhs> {
49    lhs: Lhs,
50    rhs: Rhs,
51}
52
53impl<T, U, Lhs, Rhs> Iterator for DiffIter<Lhs, Rhs>
54where
55    T: PartialEq<U>,
56    Lhs: Iterator<Item = T>,
57    Rhs: Iterator<Item = U>,
58{
59    type Item = Diff<U>;
60
61    fn next(&mut self) -> Option<Self::Item> {
62        let l = self.lhs.next();
63        let r = self.rhs.next();
64
65        match (l, r) {
66            (None, None) => None,
67            (None, Some(r)) => Some(Diff::Add(r)),
68            (Some(_), None) => Some(Diff::Remove),
69            (Some(l), Some(r)) => match l == r {
70                true => Some(Diff::Keep),
71                false => Some(Diff::Change(r)),
72            },
73        }
74    }
75}
76
77/// Iterate through the differences between each element.
78pub trait IterDiff<T>: IntoIterator<Item = T> + sealed::Sealed<T> {
79    /// Return an iterator through the differences of each element.
80    ///
81    /// ```
82    /// use iter_diff::prelude::*;
83    ///
84    /// let a = [0, 1, 2];
85    /// let b = [0, 3, 2, 3];
86    ///
87    /// let diffs: Vec<_> = a.iter_diff(b).collect();
88    /// assert_eq!(diffs.len(), 4);
89    ///
90    /// assert_eq!(diffs[0], Diff::Keep);
91    /// assert_eq!(diffs[1], Diff::Change(3));
92    /// assert_eq!(diffs[2], Diff::Keep);
93    /// assert_eq!(diffs[3], Diff::Add(3));
94    /// ```
95    fn iter_diff<U, Rhs>(
96        self,
97        rhs: Rhs,
98    ) -> DiffIter<Self::IntoIter, Rhs::IntoIter>
99    where
100        T: PartialEq<U>,
101        Rhs: IntoIterator<Item = U>;
102}
103
104impl<T, Lhs> IterDiff<T> for Lhs
105where
106    Lhs: IntoIterator<Item = T>,
107{
108    fn iter_diff<U, Rhs>(
109        self,
110        rhs: Rhs,
111    ) -> DiffIter<Lhs::IntoIter, Rhs::IntoIter>
112    where
113        T: PartialEq<U>,
114        Rhs: IntoIterator<Item = U>,
115    {
116        let lhs = self.into_iter();
117        let rhs = rhs.into_iter();
118        DiffIter { lhs, rhs }
119    }
120}
121
122mod sealed {
123    pub trait Sealed<T> {}
124
125    impl<T, I> Sealed<T> for I where I: IntoIterator<Item = T> {}
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131
132    #[test]
133    fn remove() {
134        let a = [0, 1, 2, 4];
135        let b = [0, 2];
136
137        let diffs: Vec<_> = a.iter_diff(b).collect();
138        assert_eq!(diffs.len(), 4);
139
140        assert_eq!(diffs[0], Diff::Keep);
141        assert_eq!(diffs[1], Diff::Change(2));
142        assert_eq!(diffs[2], Diff::Remove);
143        assert_eq!(diffs[3], Diff::Remove);
144    }
145
146    #[test]
147    fn add() {
148        let a = [0, 2];
149        let b = [0, 1, 2, 4];
150
151        let diffs: Vec<_> = a.iter_diff(b).collect();
152        assert_eq!(diffs.len(), 4);
153
154        assert_eq!(diffs[0], Diff::Keep);
155        assert_eq!(diffs[1], Diff::Change(1));
156        assert_eq!(diffs[2], Diff::Add(2));
157        assert_eq!(diffs[3], Diff::Add(4));
158    }
159
160    #[test]
161    fn multi_change() {
162        let a = [0, 1, 2, 3];
163        let b = [0, 3, 1, 3];
164
165        let diffs: Vec<_> = a.iter_diff(b).collect();
166        assert_eq!(diffs.len(), 4);
167
168        assert_eq!(diffs[0], Diff::Keep);
169        assert_eq!(diffs[1], Diff::Change(3));
170        assert_eq!(diffs[2], Diff::Change(1));
171        assert_eq!(diffs[3], Diff::Keep);
172    }
173
174    struct TestInt(i32);
175    impl PartialEq<i32> for TestInt {
176        fn eq(&self, other: &i32) -> bool {
177            self.0 == *other
178        }
179    }
180
181    #[test]
182    fn add_mixed() {
183        let a = [TestInt(0), TestInt(2)];
184        let b = [0, 1, 2, 4];
185
186        let diffs: Vec<_> = a.iter_diff(b).collect();
187        assert_eq!(diffs.len(), 4);
188
189        assert_eq!(diffs[0], Diff::Keep);
190        assert_eq!(diffs[1], Diff::Change(1));
191        assert_eq!(diffs[2], Diff::Add(2));
192        assert_eq!(diffs[3], Diff::Add(4));
193    }
194}