1 | //! The various iterators this crate provides. |
2 | //! |
3 | //! These iterators are not a very stable interface and you really should |
4 | //! avoid considering them to be concrete types. A lot of the iterators in |
5 | //! this crate use `impl Iterator` for this reason but restrictions in the |
6 | //! language don't allow this to be used in all places on the versions of |
7 | //! rust this crate wants to compile for. |
8 | use std::marker::PhantomData; |
9 | use std::ops::{Index, Range}; |
10 | |
11 | use crate::{Change, ChangeTag, DiffOp, DiffTag}; |
12 | |
13 | /// Iterator for [`DiffOp::iter_changes`]. |
14 | pub struct ChangesIter<'lookup, Old: ?Sized, New: ?Sized, T> { |
15 | old: &'lookup Old, |
16 | new: &'lookup New, |
17 | old_range: Range<usize>, |
18 | new_range: Range<usize>, |
19 | old_index: usize, |
20 | new_index: usize, |
21 | old_i: usize, |
22 | new_i: usize, |
23 | tag: DiffTag, |
24 | _marker: PhantomData<T>, |
25 | } |
26 | |
27 | impl<'lookup, Old, New, T> ChangesIter<'lookup, Old, New, T> |
28 | where |
29 | Old: Index<usize, Output = T> + ?Sized, |
30 | New: Index<usize, Output = T> + ?Sized, |
31 | { |
32 | pub(crate) fn new(old: &'lookup Old, new: &'lookup New, op: DiffOp) -> Self { |
33 | let (tag: DiffTag, old_range: Range, new_range: Range) = op.as_tag_tuple(); |
34 | let old_index: usize = old_range.start; |
35 | let new_index: usize = new_range.start; |
36 | let old_i: usize = old_range.start; |
37 | let new_i: usize = new_range.start; |
38 | ChangesIter { |
39 | old, |
40 | new, |
41 | old_range, |
42 | new_range, |
43 | old_index, |
44 | new_index, |
45 | old_i, |
46 | new_i, |
47 | tag, |
48 | _marker: PhantomData, |
49 | } |
50 | } |
51 | } |
52 | |
53 | impl<'lookup, Old, New, T> Iterator for ChangesIter<'lookup, Old, New, T> |
54 | where |
55 | Old: Index<usize, Output = T> + ?Sized, |
56 | New: Index<usize, Output = T> + ?Sized, |
57 | T: Clone, |
58 | { |
59 | type Item = Change<T>; |
60 | |
61 | fn next(&mut self) -> Option<Self::Item> { |
62 | match self.tag { |
63 | DiffTag::Equal => { |
64 | if self.old_i < self.old_range.end { |
65 | let value = self.old[self.old_i].clone(); |
66 | self.old_i += 1; |
67 | self.old_index += 1; |
68 | self.new_index += 1; |
69 | Some(Change { |
70 | tag: ChangeTag::Equal, |
71 | old_index: Some(self.old_index - 1), |
72 | new_index: Some(self.new_index - 1), |
73 | value, |
74 | }) |
75 | } else { |
76 | None |
77 | } |
78 | } |
79 | DiffTag::Delete => { |
80 | if self.old_i < self.old_range.end { |
81 | let value = self.old[self.old_i].clone(); |
82 | self.old_i += 1; |
83 | self.old_index += 1; |
84 | Some(Change { |
85 | tag: ChangeTag::Delete, |
86 | old_index: Some(self.old_index - 1), |
87 | new_index: None, |
88 | value, |
89 | }) |
90 | } else { |
91 | None |
92 | } |
93 | } |
94 | DiffTag::Insert => { |
95 | if self.new_i < self.new_range.end { |
96 | let value = self.new[self.new_i].clone(); |
97 | self.new_i += 1; |
98 | self.new_index += 1; |
99 | Some(Change { |
100 | tag: ChangeTag::Insert, |
101 | old_index: None, |
102 | new_index: Some(self.new_index - 1), |
103 | value, |
104 | }) |
105 | } else { |
106 | None |
107 | } |
108 | } |
109 | DiffTag::Replace => { |
110 | if self.old_i < self.old_range.end { |
111 | let value = self.old[self.old_i].clone(); |
112 | self.old_i += 1; |
113 | self.old_index += 1; |
114 | Some(Change { |
115 | tag: ChangeTag::Delete, |
116 | old_index: Some(self.old_index - 1), |
117 | new_index: None, |
118 | value, |
119 | }) |
120 | } else if self.new_i < self.new_range.end { |
121 | let value = self.new[self.new_i].clone(); |
122 | self.new_i += 1; |
123 | self.new_index += 1; |
124 | Some(Change { |
125 | tag: ChangeTag::Insert, |
126 | old_index: None, |
127 | new_index: Some(self.new_index - 1), |
128 | value, |
129 | }) |
130 | } else { |
131 | None |
132 | } |
133 | } |
134 | } |
135 | } |
136 | } |
137 | |
138 | #[cfg (feature = "text" )] |
139 | mod text { |
140 | use super::*; |
141 | |
142 | /// Iterator for [`TextDiff::iter_all_changes`](crate::TextDiff::iter_all_changes). |
143 | pub struct AllChangesIter<'slf, 'data, T: ?Sized> { |
144 | old: &'slf [&'data T], |
145 | new: &'slf [&'data T], |
146 | ops: &'slf [DiffOp], |
147 | current_iter: Option<ChangesIter<'slf, [&'data T], [&'data T], &'data T>>, |
148 | } |
149 | |
150 | impl<'slf, 'data, T> AllChangesIter<'slf, 'data, T> |
151 | where |
152 | T: 'data + ?Sized + PartialEq, |
153 | { |
154 | pub(crate) fn new( |
155 | old: &'slf [&'data T], |
156 | new: &'slf [&'data T], |
157 | ops: &'slf [DiffOp], |
158 | ) -> Self { |
159 | AllChangesIter { |
160 | old, |
161 | new, |
162 | ops, |
163 | current_iter: None, |
164 | } |
165 | } |
166 | } |
167 | |
168 | impl<'slf, 'data, T> Iterator for AllChangesIter<'slf, 'data, T> |
169 | where |
170 | T: PartialEq + 'data + ?Sized, |
171 | 'data: 'slf, |
172 | { |
173 | type Item = Change<&'data T>; |
174 | |
175 | fn next(&mut self) -> Option<Self::Item> { |
176 | loop { |
177 | if let Some(ref mut iter) = self.current_iter { |
178 | if let Some(rv) = iter.next() { |
179 | return Some(rv); |
180 | } |
181 | self.current_iter.take(); |
182 | } |
183 | if let Some((&first, rest)) = self.ops.split_first() { |
184 | self.current_iter = Some(ChangesIter::new(self.old, self.new, first)); |
185 | self.ops = rest; |
186 | } else { |
187 | return None; |
188 | } |
189 | } |
190 | } |
191 | } |
192 | } |
193 | |
194 | #[cfg (feature = "text" )] |
195 | pub use self::text::*; |
196 | |