adjacent_pair_iterator/
lib.rs

1#![no_std]
2#![deny(clippy::all)]
3#![deny(clippy::pedantic)]
4#![deny(warnings)]
5//! # adjacent-pair-iterator
6//! A library that takes an iterator and turns it into an iterator over adjacent pairs.
7//!
8//! ## Example:
9//! ```
10//! use adjacent_pair_iterator::AdjacentPairIterator;
11//!
12//! let vector = vec![1, 2, 3, 4];
13//! let mut iterator = vector.adjacent_pairs();
14//!
15//! assert_eq!((1, 2), iterator.next().unwrap());
16//! assert_eq!((2, 3), iterator.next().unwrap());
17//! assert_eq!((3, 4), iterator.next().unwrap());
18//!
19//! assert_eq!(None, iterator.next());
20//! ```
21use core::fmt::{Debug, Formatter};
22use core::iter::FusedIterator;
23
24#[cfg(test)]
25mod test_helpers;
26
27/// An iterator over adjacent pairs of values in the underlying `IteratorType`.
28///
29/// This is usually created using [`AdjacentPairIterator::adjacent_pairs`].
30#[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
121/// Extends all types implementing [`IntoIterator`] with clonable items with the `adjacent_pairs` method.
122pub trait AdjacentPairIterator {
123	type Iterator: Iterator;
124
125	/// Return an iterator of adjacent pairs in `Self`.
126	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		// NOTE: With stripped commas because this changed between 1.31 and now (1.56)
230		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}