summaryrefslogtreecommitdiff
path: root/vendor/itertools/src/zip_longest.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/itertools/src/zip_longest.rs')
-rw-r--r--vendor/itertools/src/zip_longest.rs139
1 files changed, 139 insertions, 0 deletions
diff --git a/vendor/itertools/src/zip_longest.rs b/vendor/itertools/src/zip_longest.rs
new file mode 100644
index 00000000..d4eb9a88
--- /dev/null
+++ b/vendor/itertools/src/zip_longest.rs
@@ -0,0 +1,139 @@
+use super::size_hint;
+use std::cmp::Ordering::{Equal, Greater, Less};
+use std::iter::{Fuse, FusedIterator};
+
+use crate::either_or_both::EitherOrBoth;
+
+// ZipLongest originally written by SimonSapin,
+// and dedicated to itertools https://github.com/rust-lang/rust/pull/19283
+
+/// An iterator which iterates two other iterators simultaneously
+/// and wraps the elements in [`EitherOrBoth`].
+///
+/// This iterator is *fused*.
+///
+/// See [`.zip_longest()`](crate::Itertools::zip_longest) for more information.
+#[derive(Clone, Debug)]
+#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
+pub struct ZipLongest<T, U> {
+ a: Fuse<T>,
+ b: Fuse<U>,
+}
+
+/// Create a new `ZipLongest` iterator.
+pub fn zip_longest<T, U>(a: T, b: U) -> ZipLongest<T, U>
+where
+ T: Iterator,
+ U: Iterator,
+{
+ ZipLongest {
+ a: a.fuse(),
+ b: b.fuse(),
+ }
+}
+
+impl<T, U> Iterator for ZipLongest<T, U>
+where
+ T: Iterator,
+ U: Iterator,
+{
+ type Item = EitherOrBoth<T::Item, U::Item>;
+
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ match (self.a.next(), self.b.next()) {
+ (None, None) => None,
+ (Some(a), None) => Some(EitherOrBoth::Left(a)),
+ (None, Some(b)) => Some(EitherOrBoth::Right(b)),
+ (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)),
+ }
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ size_hint::max(self.a.size_hint(), self.b.size_hint())
+ }
+
+ #[inline]
+ fn fold<B, F>(self, init: B, mut f: F) -> B
+ where
+ Self: Sized,
+ F: FnMut(B, Self::Item) -> B,
+ {
+ let Self { mut a, mut b } = self;
+ let res = a.try_fold(init, |init, a| match b.next() {
+ Some(b) => Ok(f(init, EitherOrBoth::Both(a, b))),
+ None => Err(f(init, EitherOrBoth::Left(a))),
+ });
+ match res {
+ Ok(acc) => b.map(EitherOrBoth::Right).fold(acc, f),
+ Err(acc) => a.map(EitherOrBoth::Left).fold(acc, f),
+ }
+ }
+}
+
+impl<T, U> DoubleEndedIterator for ZipLongest<T, U>
+where
+ T: DoubleEndedIterator + ExactSizeIterator,
+ U: DoubleEndedIterator + ExactSizeIterator,
+{
+ #[inline]
+ fn next_back(&mut self) -> Option<Self::Item> {
+ match self.a.len().cmp(&self.b.len()) {
+ Equal => match (self.a.next_back(), self.b.next_back()) {
+ (None, None) => None,
+ (Some(a), Some(b)) => Some(EitherOrBoth::Both(a, b)),
+ // These can only happen if .len() is inconsistent with .next_back()
+ (Some(a), None) => Some(EitherOrBoth::Left(a)),
+ (None, Some(b)) => Some(EitherOrBoth::Right(b)),
+ },
+ Greater => self.a.next_back().map(EitherOrBoth::Left),
+ Less => self.b.next_back().map(EitherOrBoth::Right),
+ }
+ }
+
+ fn rfold<B, F>(self, mut init: B, mut f: F) -> B
+ where
+ F: FnMut(B, Self::Item) -> B,
+ {
+ let Self { mut a, mut b } = self;
+ let a_len = a.len();
+ let b_len = b.len();
+ match a_len.cmp(&b_len) {
+ Equal => {}
+ Greater => {
+ init = a
+ .by_ref()
+ .rev()
+ .take(a_len - b_len)
+ .map(EitherOrBoth::Left)
+ .fold(init, &mut f)
+ }
+ Less => {
+ init = b
+ .by_ref()
+ .rev()
+ .take(b_len - a_len)
+ .map(EitherOrBoth::Right)
+ .fold(init, &mut f)
+ }
+ }
+ a.rfold(init, |acc, item_a| {
+ f(acc, EitherOrBoth::Both(item_a, b.next_back().unwrap()))
+ })
+ }
+}
+
+impl<T, U> ExactSizeIterator for ZipLongest<T, U>
+where
+ T: ExactSizeIterator,
+ U: ExactSizeIterator,
+{
+}
+
+impl<T, U> FusedIterator for ZipLongest<T, U>
+where
+ T: Iterator,
+ U: Iterator,
+{
+}