use std::cmp; use std::iter::IntoIterator; #[doc(hidden)] #[derive(Clone, Debug)] #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] pub struct ZipOption { a: A, b: B, // index and len are only used by the specialized version of zip index: usize, len: usize, } /// Zips to iterators together to the longest length /// via Option<(Option, Option)> pub trait ZipOpt { /// Zip to iterators to longest length via Option<(Option, Option)> results. /// # Example /// ``` /// use array_tool::iter::ZipOpt; /// /// let a = vec!["a","b","c", "d"]; /// let b = vec!["c","d"]; /// let mut x = a.iter().zip_option(b.iter()); /// /// assert_eq!(x.next(), Some((Some(&"a"), Some(&"c")))); /// assert_eq!(x.next(), Some((Some(&"b"), Some(&"d")))); /// assert_eq!(x.next(), Some((Some(&"c"), None))); /// assert_eq!(x.next(), Some((Some(&"d"), None))); /// assert_eq!(x.next(), None); /// ``` /// /// # Output /// ```text /// vec![ "a", "b", "c", "d" ] /// ``` fn zip_option(self, other: U) -> ZipOption where Self: Sized, U: IntoIterator; } impl ZipOpt for I { #[inline] fn zip_option(self, other: U) -> ZipOption where Self: Sized, U: IntoIterator { ZipOption::new(self, other.into_iter()) } } impl Iterator for ZipOption where A: Iterator, B: Iterator { type Item = (Option, Option); #[inline] fn next(&mut self) -> Option { ZipImpl::next(self) } #[inline] fn size_hint(&self) -> (usize, Option) { ZipImpl::size_hint(self) } #[inline] fn nth(&mut self, n: usize) -> Option { ZipImpl::nth(self, n) } } #[doc(hidden)] impl DoubleEndedIterator for ZipOption where A: DoubleEndedIterator + ExactSizeIterator, B: DoubleEndedIterator + ExactSizeIterator, { #[inline] fn next_back(&mut self) -> Option<(Option, Option)> { ZipImpl::next_back(self) } } #[doc(hidden)] trait ZipImpl { type Item; fn new(a: A, b: B) -> Self; fn next(&mut self) -> Option; fn size_hint(&self) -> (usize, Option); fn nth(&mut self, n: usize) -> Option; fn super_nth(&mut self, mut n: usize) -> Option { while let Some(x) = self.next() { if n == 0 { return Some(x) } n -= 1; } None } fn next_back(&mut self) -> Option where A: DoubleEndedIterator + ExactSizeIterator, B: DoubleEndedIterator + ExactSizeIterator; } #[doc(hidden)] impl ZipImpl for ZipOption where A: Iterator, B: Iterator { type Item = (Option, Option); fn new(a: A, b: B) -> Self { ZipOption { a, b, index: 0, // unused len: 0, // unused } } #[inline] fn next(&mut self) -> Option<(Option, Option)> { let first = self.a.next(); let second = self.b.next(); if first.is_some() || second.is_some() { Some((first, second)) } else { None } } #[inline] fn nth(&mut self, n: usize) -> Option { self.super_nth(n) } #[inline] fn next_back(&mut self) -> Option<(Option, Option)> where A: DoubleEndedIterator + ExactSizeIterator, B: DoubleEndedIterator + ExactSizeIterator { let a_sz = self.a.len(); let b_sz = self.b.len(); if a_sz != b_sz { // Adjust a, b to equal length if a_sz > b_sz { for _ in 0..a_sz - b_sz { self.a.next_back(); } } else { for _ in 0..b_sz - a_sz { self.b.next_back(); } } } match (self.a.next_back(), self.b.next_back()) { (None, None) => None, (f,s) => Some((f, s)), } } #[inline] fn size_hint(&self) -> (usize, Option) { let (a_lower, a_upper) = self.a.size_hint(); let (b_lower, b_upper) = self.b.size_hint(); let lower = cmp::min(a_lower, b_lower); let upper = match (a_upper, b_upper) { (Some(x), Some(y)) => Some(cmp::max(x,y)), (Some(x), None) => Some(x), (None, Some(y)) => Some(y), (None, None) => None }; (lower, upper) } }