similar/
iter.rs

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.
8use std::marker::PhantomData;
9use std::ops::{Index, Range};
10
11use crate::{Change, ChangeTag, DiffOp, DiffTag};
12
13/// Iterator for [`DiffOp::iter_changes`].
14pub 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
27impl<'lookup, Old, New, T> ChangesIter<'lookup, Old, New, T>
28where
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, old_range, new_range) = op.as_tag_tuple();
34        let old_index = old_range.start;
35        let new_index = new_range.start;
36        let old_i = old_range.start;
37        let new_i = 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
53impl<Old, New, T> Iterator for ChangesIter<'_, Old, New, T>
54where
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")]
139mod 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")]
195pub use self::text::*;