mz_ore/
iter.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License in the LICENSE file at the
6// root of this repository, or online at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16//! Iterator utilities.
17
18use std::fmt::Debug;
19use std::iter::{self, Chain, Once, Peekable};
20
21/// Extension methods for iterators.
22pub trait IteratorExt
23where
24    Self: Iterator + Sized,
25{
26    /// Chains a single `item` onto the end of this iterator.
27    ///
28    /// Equivalent to `self.chain(iter::once(item))`.
29    fn chain_one(self, item: Self::Item) -> Chain<Self, Once<Self::Item>> {
30        self.chain(iter::once(item))
31    }
32
33    /// Reports whether all the elements of the iterator are the same.
34    ///
35    /// This condition is trivially true for iterators with zero or one elements.
36    fn all_equal(mut self) -> bool
37    where
38        Self::Item: PartialEq,
39    {
40        match self.next() {
41            None => true,
42            Some(v1) => self.all(|v2| v1 == v2),
43        }
44    }
45
46    /// Converts the the iterator into an `ExactSizeIterator` reporting the given size.
47    ///
48    /// The caller is responsible for providing the correct size of the iterator! Providing an
49    /// incorrect size value will lead to panics and/or incorrect responses to size queries.
50    ///
51    /// # Panics
52    ///
53    /// Panics if the given length is not consistent with this iterator's `size_hint`.
54    fn exact_size(self, len: usize) -> ExactSize<Self> {
55        let (lower, upper) = self.size_hint();
56        assert!(
57            lower <= len && upper.map_or(true, |upper| upper >= len),
58            "provided length {len} inconsistent with `size_hint`: {:?}",
59            (lower, upper)
60        );
61
62        ExactSize { inner: self, len }
63    }
64
65    /// Wrap this iterator with one that yields a tuple of the iterator element and the extra
66    /// value on each iteration. The extra value is cloned for each but the last `Some` element
67    /// returned.
68    ///
69    /// This is useful to provide an owned extra value to each iteration, but only clone it
70    /// when necessary.
71    ///
72    /// NOTE: Once the iterator starts producing `None` values, the extra value will be consumed
73    /// and no longer be available. This should not be used for iterators that may produce
74    /// `Some` values after producing `None`.
75    fn repeat_clone<A: Clone>(self, extra_val: A) -> RepeatClone<Self, A> {
76        RepeatClone {
77            iter: self.peekable(),
78            extra_val: Some(extra_val),
79        }
80    }
81}
82
83impl<I> IteratorExt for I where I: Iterator {}
84
85/// Iterator type returned by [`IteratorExt::exact_size`].
86#[derive(Debug)]
87pub struct ExactSize<I> {
88    inner: I,
89    len: usize,
90}
91
92impl<I: Iterator> Iterator for ExactSize<I> {
93    type Item = I::Item;
94
95    fn next(&mut self) -> Option<Self::Item> {
96        self.len = self.len.saturating_sub(1);
97        self.inner.next()
98    }
99
100    fn size_hint(&self) -> (usize, Option<usize>) {
101        (self.len, Some(self.len))
102    }
103}
104
105impl<I: Iterator> ExactSizeIterator for ExactSize<I> {}
106
107/// Iterator type returned by [`IteratorExt::repeat_clone`].
108pub struct RepeatClone<I: Iterator, A> {
109    iter: Peekable<I>,
110    extra_val: Option<A>,
111}
112
113impl<I: Iterator, A: Clone> Iterator for RepeatClone<I, A> {
114    type Item = (I::Item, A);
115
116    fn next(&mut self) -> Option<Self::Item> {
117        let next = self.iter.next()?;
118
119        // Clone the extra_val only if there is an item to return on the next call to `next`.
120        let val = match self.iter.peek() {
121            Some(_) => self.extra_val.clone(),
122            None => self.extra_val.take(),
123        };
124
125        // We should always return a value if there is a current element.
126        Some((next, val.expect("RepeatClone invariant violated")))
127    }
128}
129
130impl<I: Iterator<Item: Debug> + Debug, A: Debug> Debug for RepeatClone<I, A> {
131    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
132        f.debug_struct("RepeatClone")
133            .field("iter", &self.iter)
134            .field("extra_val", &self.extra_val)
135            .finish()
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use crate::iter::IteratorExt;
142
143    #[crate::test]
144    fn test_all_equal() {
145        let empty: [i64; 0] = [];
146        assert!(empty.iter().all_equal());
147        assert!([1].iter().all_equal());
148        assert!([1, 1].iter().all_equal());
149        assert!(![1, 2].iter().all_equal());
150    }
151}