1#![deny(missing_docs)]
24#![deny(clippy::all)]
25
26pub mod prelude;
27
28#[derive(Debug, Hash, Clone, Copy, PartialOrd, Ord, PartialEq, Eq)]
30pub enum Diff<T> {
31 Change(T),
35 Remove,
38 Keep,
41 Add(T),
44}
45
46#[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
77pub trait IterDiff<T>: IntoIterator<Item = T> + sealed::Sealed<T> {
79 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}