From c1ec796f40097d6b89532cd982e5456fbbd8cc1c Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 19 Jan 2024 16:55:28 -0800 Subject: [PATCH] Add a new type for the factored iterator --- src/iterator.rs | 178 +++++++++++++++++++++++++++++++++++++++++++++--- src/lib.rs | 1 + 2 files changed, 169 insertions(+), 10 deletions(-) diff --git a/src/iterator.rs b/src/iterator.rs index a20d67f..26d8aaf 100644 --- a/src/iterator.rs +++ b/src/iterator.rs @@ -84,23 +84,25 @@ impl Either { /// ``` // TODO(MSRV): doc(alias) was stabilized in Rust 1.48 // #[doc(alias = "transpose")] - pub fn factor_iter( - self, - ) -> Either< - core::iter::Map Either>, - core::iter::Map Either>, - > + pub fn factor_iter(self) -> IterEither where L: IntoIterator, R: IntoIterator, { - self.map_either( - |l| l.into_iter().map(Either::Left), - |r| r.into_iter().map(Either::Right), - ) + IterEither { + inner: self.map_either(L::into_iter, R::into_iter), + } } } +/// Iterator that maps left or right iterators to corresponding `Either`-wrapped items. +/// +/// This struct is created by the [`Either::factor_iter`] method. +#[derive(Clone, Debug)] +pub struct IterEither { + inner: Either, +} + impl Extend for Either where L: Extend, @@ -252,3 +254,159 @@ where R: iter::FusedIterator, { } + +macro_rules! map_either { + ($value:expr, $pattern:pat => $result:expr) => { + match $value { + Left($pattern) => Left($result), + Right($pattern) => Right($result), + } + }; +} + +macro_rules! wrap_either { + ($value:expr => $( $tail:tt )*) => { + match $value { + Left(inner) => inner.map(Left) $($tail)*, + Right(inner) => inner.map(Right) $($tail)*, + } + }; +} + +impl Iterator for IterEither +where + L: Iterator, + R: Iterator, +{ + type Item = Either; + + fn next(&mut self) -> Option { + Some(map_either!(self.inner, ref mut inner => inner.next()?)) + } + + fn size_hint(&self) -> (usize, Option) { + for_both!(self.inner, ref inner => inner.size_hint()) + } + + fn fold(self, init: Acc, f: G) -> Acc + where + G: FnMut(Acc, Self::Item) -> Acc, + { + wrap_either!(self.inner => .fold(init, f)) + } + + fn for_each(self, f: F) + where + F: FnMut(Self::Item), + { + wrap_either!(self.inner => .for_each(f)) + } + + fn count(self) -> usize { + for_both!(self.inner, inner => inner.count()) + } + + fn last(self) -> Option { + Some(map_either!(self.inner, inner => inner.last()?)) + } + + fn nth(&mut self, n: usize) -> Option { + Some(map_either!(self.inner, ref mut inner => inner.nth(n)?)) + } + + fn collect(self) -> B + where + B: iter::FromIterator, + { + wrap_either!(self.inner => .collect()) + } + + fn partition(self, f: F) -> (B, B) + where + B: Default + Extend, + F: FnMut(&Self::Item) -> bool, + { + wrap_either!(self.inner => .partition(f)) + } + + fn all(&mut self, f: F) -> bool + where + F: FnMut(Self::Item) -> bool, + { + wrap_either!(&mut self.inner => .all(f)) + } + + fn any(&mut self, f: F) -> bool + where + F: FnMut(Self::Item) -> bool, + { + wrap_either!(&mut self.inner => .any(f)) + } + + fn find

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + wrap_either!(&mut self.inner => .find(predicate)) + } + + fn find_map(&mut self, f: F) -> Option + where + F: FnMut(Self::Item) -> Option, + { + wrap_either!(&mut self.inner => .find_map(f)) + } + + fn position

(&mut self, predicate: P) -> Option + where + P: FnMut(Self::Item) -> bool, + { + wrap_either!(&mut self.inner => .position(predicate)) + } +} + +impl DoubleEndedIterator for IterEither +where + L: DoubleEndedIterator, + R: DoubleEndedIterator, +{ + fn next_back(&mut self) -> Option { + Some(map_either!(self.inner, ref mut inner => inner.next_back()?)) + } + + // TODO(MSRV): This was stabilized in Rust 1.37 + // fn nth_back(&mut self, n: usize) -> Option { + // Some(map_either!(self.inner, ref mut inner => inner.nth_back(n)?)) + // } + + fn rfold(self, init: Acc, f: G) -> Acc + where + G: FnMut(Acc, Self::Item) -> Acc, + { + wrap_either!(self.inner => .rfold(init, f)) + } + + fn rfind

(&mut self, predicate: P) -> Option + where + P: FnMut(&Self::Item) -> bool, + { + wrap_either!(&mut self.inner => .rfind(predicate)) + } +} + +impl ExactSizeIterator for IterEither +where + L: ExactSizeIterator, + R: ExactSizeIterator, +{ + fn len(&self) -> usize { + for_both!(self.inner, ref inner => inner.len()) + } +} + +impl iter::FusedIterator for IterEither +where + L: iter::FusedIterator, + R: iter::FusedIterator, +{ +} diff --git a/src/lib.rs b/src/lib.rs index d480a61..71cd239 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -929,6 +929,7 @@ impl Either { } mod iterator; +pub use iterator::IterEither; /// Convert from `Result` to `Either` with `Ok => Right` and `Err => Left`. impl From> for Either {