use std::cmp::Ordering::{Equal, Greater, Less}; use super::size_hint; 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 /// /// 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 { a: Fuse, b: Fuse, } /// Create a new `ZipLongest` iterator. pub fn zip_longest(a: T, b: U) -> ZipLongest where T: Iterator, U: Iterator { ZipLongest { a: a.fuse(), b: b.fuse(), } } impl Iterator for ZipLongest where T: Iterator, U: Iterator { type Item = EitherOrBoth; #[inline] fn next(&mut self) -> Option { 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) { size_hint::max(self.a.size_hint(), self.b.size_hint()) } } impl DoubleEndedIterator for ZipLongest where T: DoubleEndedIterator + ExactSizeIterator, U: DoubleEndedIterator + ExactSizeIterator { #[inline] fn next_back(&mut self) -> Option { 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), } } } impl ExactSizeIterator for ZipLongest where T: ExactSizeIterator, U: ExactSizeIterator {} impl FusedIterator for ZipLongest where T: Iterator, U: Iterator {}