Skip to content

Commit

Permalink
Split factor_iter into owned/ref/mut variations
Browse files Browse the repository at this point in the history
  • Loading branch information
cuviper committed Jan 28, 2024
1 parent 40a1ff0 commit 7f7b542
Showing 1 changed file with 96 additions and 30 deletions.
126 changes: 96 additions & 30 deletions src/iterator.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,30 @@
use super::{for_both, Either, Left, Right};
use core::iter;

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<L, R> Either<L, R> {
/// Convert the inner value to an iterator.
///
/// This requires the `Left` and `Right` iterators to have the same item type.
/// See [`factor_into_iter`][Either::factor_into_iter] to iterate different types.
///
/// ```
/// use either::*;
///
Expand All @@ -18,14 +39,14 @@ impl<L, R> Either<L, R> {
L: IntoIterator,
R: IntoIterator<Item = L::Item>,
{
match self {
Left(l) => Left(l.into_iter()),
Right(r) => Right(r.into_iter()),
}
map_either!(self, inner => inner.into_iter())
}

/// Borrow the inner value as an iterator.
///
/// This requires the `Left` and `Right` iterators to have the same item type.
/// See [`factor_iter`][Either::factor_iter] to iterate different types.
///
/// ```
/// use either::*;
///
Expand All @@ -41,11 +62,14 @@ impl<L, R> Either<L, R> {
for<'a> &'a L: IntoIterator,
for<'a> &'a R: IntoIterator<Item = <&'a L as IntoIterator>::Item>,
{
self.as_ref().into_iter()
map_either!(self, inner => inner.into_iter())
}

/// Mutably borrow the inner value as an iterator.
///
/// This requires the `Left` and `Right` iterators to have the same item type.
/// See [`factor_iter_mut`][Either::factor_iter_mut] to iterate different types.
///
/// ```
/// use either::*;
///
Expand All @@ -69,35 +93,95 @@ impl<L, R> Either<L, R> {
for<'a> &'a mut L: IntoIterator,
for<'a> &'a mut R: IntoIterator<Item = <&'a mut L as IntoIterator>::Item>,
{
self.as_mut().into_iter()
map_either!(self, inner => inner.into_iter())
}

/// Factors an `Either` of `Iterator`s to be an `Iterator` of `Either`s
/// Converts an `Either` of `Iterator`s to be an `Iterator` of `Either`s
///
/// Unlike [`into_iter`][Either::into_iter], this does not require the
/// `Left` and `Right` iterators to have the same item type.
///
/// ```
/// use either::*;
/// let left: Either<_, Vec<u8>> = Left(&["hello"]);
/// assert_eq!(left.factor_iter().next(), Some(Left(&"hello")));
/// assert_eq!(left.factor_into_iter().next(), Some(Left(&"hello")));
/// let right: Either<&[&str], _> = Right(vec![0, 1]);
/// assert_eq!(right.factor_iter().collect::<Vec<_>>(), vec![Right(0), Right(1)]);
/// assert_eq!(right.factor_into_iter().collect::<Vec<_>>(), vec![Right(0), Right(1)]);
///
/// ```
// TODO(MSRV): doc(alias) was stabilized in Rust 1.48
// #[doc(alias = "transpose")]
pub fn factor_iter(self) -> IterEither<L::IntoIter, R::IntoIter>
pub fn factor_into_iter(self) -> IterEither<L::IntoIter, R::IntoIter>
where
L: IntoIterator,
R: IntoIterator,
{
IterEither {
inner: self.map_either(L::into_iter, R::into_iter),
inner: map_either!(self, inner => inner.into_iter()),
}
}

/// Borrows an `Either` of `Iterator`s to be an `Iterator` of `Either`s
///
/// Unlike [`iter`][Either::iter], this does not require the
/// `Left` and `Right` iterators to have the same item type.
///
/// ```
/// use either::*;
/// let left: Either<_, Vec<u8>> = Left(["hello"]);
/// assert_eq!(left.factor_iter().next(), Some(Left(&"hello")));
/// let right: Either<[&str; 2], _> = Right(vec![0, 1]);
/// assert_eq!(right.factor_iter().collect::<Vec<_>>(), vec![Right(&0), Right(&1)]);
///
/// ```
pub fn factor_iter(
&self,
) -> IterEither<<&L as IntoIterator>::IntoIter, <&R as IntoIterator>::IntoIter>
where
for<'a> &'a L: IntoIterator,
for<'a> &'a R: IntoIterator,
{
IterEither {
inner: map_either!(self, inner => inner.into_iter()),
}
}

/// Mutably borrows an `Either` of `Iterator`s to be an `Iterator` of `Either`s
///
/// Unlike [`iter_mut`][Either::iter_mut], this does not require the
/// `Left` and `Right` iterators to have the same item type.
///
/// ```
/// use either::*;
/// let mut left: Either<_, Vec<u8>> = Left(["hello"]);
/// left.factor_iter_mut().for_each(|x| *x.unwrap_left() = "goodbye");
/// assert_eq!(left, Left(["goodbye"]));
/// let mut right: Either<[&str; 2], _> = Right(vec![0, 1, 2]);
/// right.factor_iter_mut().for_each(|x| if let Right(r) = x { *r = -*r; });
/// assert_eq!(right, Right(vec![0, -1, -2]));
///
/// ```
pub fn factor_iter_mut(
&mut self,
) -> IterEither<<&mut L as IntoIterator>::IntoIter, <&mut R as IntoIterator>::IntoIter>
where
for<'a> &'a mut L: IntoIterator,
for<'a> &'a mut R: IntoIterator,
{
IterEither {
inner: map_either!(self, inner => inner.into_iter()),
}
}
}

/// Iterator that maps left or right iterators to corresponding `Either`-wrapped items.
///
/// This struct is created by the [`Either::factor_iter`] method.
/// This struct is created by the [`Either::factor_into_iter`],
/// [`factor_iter`][Either::factor_iter],
/// and [`factor_iter_mut`][Either::factor_iter_mut] methods.
#[derive(Clone, Debug)]
pub struct IterEither<L, R> {
inner: Either<L, R>,
Expand Down Expand Up @@ -255,24 +339,6 @@ where
{
}

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<L, R> Iterator for IterEither<L, R>
where
L: Iterator,
Expand Down

0 comments on commit 7f7b542

Please sign in to comment.