1 // Copyright 2023 Google LLC
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 at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 /// Zips up two iterators into a single iterator of pairs.
16 ///
17 /// This is identical to [`Iterator::zip`] except that this version allows the
18 /// caller to determine whether the two iterators had mismatching sizes using
19 /// the method [`ZippedIterator::has_size_mismatch`].
20 ///
21 /// [`Iterator::zip`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.zip
zip<I1, I2>(left: I1, right: I2) -> ZippedIterator<I1, I2>22 pub(crate) fn zip<I1, I2>(left: I1, right: I2) -> ZippedIterator<I1, I2> {
23     ZippedIterator { left, right, has_size_mismatch: false, consumed_elements: 0 }
24 }
25 
26 /// An iterator over pairs of the elements of two constituent iterators, which
27 /// keeps track of whether the two iterators have the same size.
28 ///
29 /// This is identical to [`Zip`] except that it allows the caller to determine
30 /// whether the two iterators had mismatching sizes using the method
31 /// [`ZippedIterator::has_size_mismatch`].
32 ///
33 /// [`Zip`]: https://doc.rust-lang.org/std/iter/struct.Zip.html
34 pub(crate) struct ZippedIterator<I1, I2> {
35     left: I1,
36     right: I2,
37     has_size_mismatch: bool,
38     consumed_elements: usize,
39 }
40 
41 impl<I1: Iterator, I2> ZippedIterator<I1, I2> {
42     /// Returns whether a mismatch in the two sizes of the two iterators was
43     /// detected during iteration.
44     ///
45     /// This returns `true` if and only if, at some previous call to
46     /// [`Iterator::next`] on this instance, one of the constituent iterators
47     /// had a next element and the other did not.
48     ///
49     /// [`Iterator::next`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html#tymethod.next
has_size_mismatch(&self) -> bool50     pub(crate) fn has_size_mismatch(&self) -> bool {
51         self.has_size_mismatch
52     }
53 
54     /// Returns the number of elements in the left iterator.
55     ///
56     /// This iterates through the remainder of the left iterator if necessary in
57     /// order to get the true number of elements. It therefore consumes `self`.
left_size(mut self) -> usize58     pub(crate) fn left_size(mut self) -> usize {
59         self.consumed_elements + self.left.by_ref().count()
60     }
61 }
62 
63 impl<I1: Iterator, I2: Iterator> Iterator for ZippedIterator<I1, I2> {
64     type Item = (I1::Item, I2::Item);
65 
next(&mut self) -> Option<(I1::Item, I2::Item)>66     fn next(&mut self) -> Option<(I1::Item, I2::Item)> {
67         match (self.left.next(), self.right.next()) {
68             (Some(v1), Some(v2)) => {
69                 self.consumed_elements += 1;
70                 Some((v1, v2))
71             }
72             (Some(_), None) => {
73                 // Consumed elements counts only elements from self.left
74                 self.consumed_elements += 1;
75                 self.has_size_mismatch = true;
76                 None
77             }
78             (None, Some(_)) => {
79                 self.has_size_mismatch = true;
80                 None
81             }
82             (None, None) => None,
83         }
84     }
85 }
86