adjacent_pair_iterator/
lib.rs1#![no_std]
2#![deny(clippy::all)]
3#![deny(clippy::pedantic)]
4#![deny(warnings)]
5use core::fmt::{Debug, Formatter};
22use core::iter::FusedIterator;
23
24#[cfg(test)]
25mod test_helpers;
26
27#[derive(Clone)]
31pub struct AdjacentPairs<IteratorType: Iterator> {
32 iterator: IteratorType,
33 last_item: Option<IteratorType::Item>,
34}
35
36impl<IteratorType> AdjacentPairs<IteratorType>
37where
38 IteratorType: Iterator,
39 IteratorType::Item: Clone,
40{
41 fn new(iterator: IteratorType) -> AdjacentPairs<IteratorType> {
42 AdjacentPairs {
43 iterator,
44 last_item: None,
45 }
46 }
47
48 fn remaining_pairs_for_given_size(&self, size: usize) -> usize {
49 let remaining_elements = size + self.last_item.is_some() as usize;
50 if remaining_elements > 0 {
51 remaining_elements - 1
52 } else {
53 0
54 }
55 }
56}
57
58impl<IteratorType> Iterator for AdjacentPairs<IteratorType>
59where
60 IteratorType: Iterator,
61 IteratorType::Item: Clone,
62{
63 type Item = (IteratorType::Item, IteratorType::Item);
64
65 fn next(&mut self) -> Option<Self::Item> {
66 let last_item = match self.last_item.take() {
67 Some(item) => item,
68 None => self.iterator.next()?,
69 };
70
71 let current_item = self.iterator.next()?;
72 self.last_item = Some(current_item.clone());
73 Some((last_item, current_item))
74 }
75
76 fn size_hint(&self) -> (usize, Option<usize>) {
77 let (lower, upper) = self.iterator.size_hint();
78 (
79 self.remaining_pairs_for_given_size(lower),
80 upper.map(|upper| self.remaining_pairs_for_given_size(upper)),
81 )
82 }
83}
84
85impl<IteratorType> Debug for AdjacentPairs<IteratorType>
86where
87 IteratorType: Iterator + Debug,
88{
89 fn fmt(&self, formatter: &mut Formatter) -> core::fmt::Result {
90 formatter
91 .debug_struct("AdjacentPairs")
92 .field("iterator", &self.iterator)
93 .finish()
94 }
95}
96
97impl<IteratorType> FusedIterator for AdjacentPairs<IteratorType>
98where
99 IteratorType: FusedIterator,
100 IteratorType::Item: Clone,
101{
102}
103
104impl<IteratorType> ExactSizeIterator for AdjacentPairs<IteratorType>
105where
106 IteratorType: ExactSizeIterator,
107 IteratorType::Item: Clone,
108{
109}
110
111impl<Iterable> From<Iterable> for AdjacentPairs<Iterable::IntoIter>
112where
113 Iterable: IntoIterator,
114 Iterable::Item: Clone,
115{
116 fn from(iterable: Iterable) -> Self {
117 Self::new(iterable.into_iter())
118 }
119}
120
121pub trait AdjacentPairIterator {
123 type Iterator: Iterator;
124
125 fn adjacent_pairs(self) -> AdjacentPairs<Self::Iterator>;
127}
128
129impl<Iterable> AdjacentPairIterator for Iterable
130where
131 Iterable: IntoIterator,
132 Iterable::Item: Clone,
133{
134 type Iterator = <Self as IntoIterator>::IntoIter;
135
136 fn adjacent_pairs(self) -> AdjacentPairs<Self::Iterator> {
137 self.into()
138 }
139}
140
141#[cfg(test)]
142mod tests {
143 use crate::test_helpers::iterator::NoStdIntoIterator;
144 use crate::test_helpers::string::NoStdString;
145 use crate::{AdjacentPairIterator, AdjacentPairs};
146
147 #[test]
148 fn should_provide_nothing_without_items() {
149 let array: [i32; 0] = [];
150 let mut iterator = array.iter().adjacent_pairs();
151
152 assert_eq!(None, iterator.next());
153 }
154
155 #[test]
156 fn should_provide_nothing_for_only_one_input() {
157 let array = [1];
158 let mut iterator = array.iter().adjacent_pairs();
159
160 assert_eq!(None, iterator.next());
161 }
162
163 #[test]
164 fn should_provide_pair_for_two_inputs() {
165 let array = [1, 2];
166 let mut iterator = array.iter().adjacent_pairs();
167
168 assert_eq!(Some((&1, &2)), iterator.next());
169 assert_eq!(None, iterator.next());
170 }
171
172 #[test]
173 fn should_provide_two_pairs_for_three_inputs() {
174 let array = [1, 2, 3];
175 let mut iterator = array.iter().adjacent_pairs();
176
177 assert_eq!(Some((&1, &2)), iterator.next());
178 assert_eq!(Some((&2, &3)), iterator.next());
179 assert_eq!(None, iterator.next());
180 }
181
182 #[test]
183 fn should_provide_many_pairs() {
184 let array = [1, 2, 3, 4, 5, 6];
185 let mut iterator = array.iter().adjacent_pairs();
186
187 assert_eq!(Some((&1, &2)), iterator.next());
188 assert_eq!(Some((&2, &3)), iterator.next());
189 assert_eq!(Some((&3, &4)), iterator.next());
190 assert_eq!(Some((&4, &5)), iterator.next());
191 assert_eq!(Some((&5, &6)), iterator.next());
192 assert_eq!(None, iterator.next());
193 }
194
195 #[test]
196 fn should_work_with_into_iterator() {
197 let iterable = NoStdIntoIterator::from([1, 2]);
198 let mut iterator = iterable.adjacent_pairs();
199
200 assert_eq!(Some((1, 2)), iterator.next());
201 assert_eq!(None, iterator.next());
202 }
203
204 #[test]
205 fn should_update_its_size_hint() {
206 let array = [0; 5];
207 let mut iterator = array.iter().adjacent_pairs();
208
209 assert_eq!((4, Some(4)), iterator.size_hint());
210 iterator.next();
211 assert_eq!((3, Some(3)), iterator.size_hint());
212 iterator.next();
213 assert_eq!((2, Some(2)), iterator.size_hint());
214 iterator.next();
215 assert_eq!((1, Some(1)), iterator.size_hint());
216 iterator.next();
217 assert_eq!((0, Some(0)), iterator.size_hint());
218 assert!(iterator.next().is_none());
219 }
220
221 #[test]
222 fn should_debug_print() {
223 let array = [1, 2];
224 let iterator = array.iter().adjacent_pairs();
225
226 let debug_output = NoStdString::format(format_args!("{:?}", iterator)).unwrap();
227 assert_eq!("AdjacentPairs { iterator: Iter([1, 2]) }", debug_output);
228
229 let expected_pretty_debug_output = r#"AdjacentPairs {
231 iterator: Iter(
232 [
233 1
234 2
235 ]
236 )
237}"#;
238 let pretty_debug_output_without_commas = NoStdString::format(format_args!("{:#?}", iterator))
239 .unwrap()
240 .removing(b',');
241 assert_eq!(expected_pretty_debug_output, pretty_debug_output_without_commas);
242 }
243
244 #[test]
245 fn should_convert_from_iterable() {
246 let iterable = NoStdIntoIterator::from([1, 2]);
247 let mut iterator: AdjacentPairs<_> = From::from(iterable);
248 assert_eq!(Some((1, 2)), iterator.next());
249 }
250}