Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added either::map_both, either:try_map_both #109

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ where
type Item = Either<L::Item, R::Item>;

fn next(&mut self) -> Option<Self::Item> {
Some(map_either!(self.inner, ref mut inner => inner.next()?))
Some(map_both!(self.inner, ref mut inner => inner.next()?))
}

fn size_hint(&self) -> (usize, Option<usize>) {
Expand All @@ -211,11 +211,11 @@ where
}

fn last(self) -> Option<Self::Item> {
Some(map_either!(self.inner, inner => inner.last()?))
Some(map_both!(self.inner, inner => inner.last()?))
}

fn nth(&mut self, n: usize) -> Option<Self::Item> {
Some(map_either!(self.inner, ref mut inner => inner.nth(n)?))
Some(map_both!(self.inner, ref mut inner => inner.nth(n)?))
}

fn collect<B>(self) -> B
Expand Down Expand Up @@ -275,11 +275,11 @@ where
R: DoubleEndedIterator,
{
fn next_back(&mut self) -> Option<Self::Item> {
Some(map_either!(self.inner, ref mut inner => inner.next_back()?))
Some(map_both!(self.inner, ref mut inner => inner.next_back()?))
}

fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
Some(map_either!(self.inner, ref mut inner => inner.nth_back(n)?))
Some(map_both!(self.inner, ref mut inner => inner.nth_back(n)?))
}

fn rfold<Acc, G>(self, init: Acc, f: G) -> Acc
Expand Down
157 changes: 142 additions & 15 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ pub enum Either<L, R> {
///
/// Syntax: `either::for_both!(` *expression* `,` *pattern* `=>` *expression* `)`
///
/// Unlike [`map_both!`], this macro converges both variants to the type returned by the expression.
///
/// # Example
///
/// ```
Expand Down Expand Up @@ -87,6 +89,99 @@ macro_rules! for_both {
};
}

/// Evaluate the provided expression for both [`Either::Left`] and [`Either::Right`],
/// returning an [`Either`] with the results.
///
/// This macro is useful in cases where both sides of [`Either`] can be interacted with
/// in the same way even though the don't share the same type.
///
/// Syntax: `either::map_both!(` *expression* `,` *pattern* `=>` *expression* `)`
///
/// Unlike [`for_both!`], this macro returns an [`Either`] with the results of the expressions.
///
/// # Example
///
/// ```
/// use either::Either;
///
/// struct Wrapper<T>(T);
///
/// fn wrap(owned_or_borrowed: Either<String, &'static str>) -> Either<Wrapper<String>, Wrapper<&'static str>> {
/// either::map_both!(owned_or_borrowed, s => Wrapper(s))
/// }
/// ```
#[macro_export]
macro_rules! map_both {
($value:expr, $pattern:pat => $result:expr) => {
match $value {
$crate::Either::Left($pattern) => $crate::Either::Left($result),
$crate::Either::Right($pattern) => $crate::Either::Right($result),
}
};
}

/// Evaluate the provided expression for both [`Either::Left`] and [`Either::Right`],
/// returning a `T`: [`Try`] where the [`Try::Output`] variant is an [`Either`] with outputs.
///
/// This macro is useful in cases where both sides of [`Either`] can be interacted with
/// in the same way even though the don't share the same type.
///
/// `either::try_map_both!(` *expression* `,` *pattern* `=>` *expression* `)`
///
/// Unlike [`map_both!`], this macro returns a [`Try`] where the [`Try::Output`] variant is an
/// [`Either`] with the outputs.
///
/// # Examples
///
/// Works with [`Result`]:
///
/// ```
/// use either::Either;
///
/// fn wrap(owned_or_borrowed: Either<String, &'static str>) -> Result<Either<String, &'static str>, ()> {
/// either::try_map_both!(owned_or_borrowed, s => Ok(s))
/// }
/// ```
///
/// Works with [`Option`]:
///
/// ```
/// use either::Either;
///
/// fn wrap(owned_or_borrowed: Either<String, &'static str>) -> Option<Either<String, &'static str>> {
/// either::try_map_both!(owned_or_borrowed, s => Some(s))
/// }
/// ```
///
/// If you want to see another [`Try`](https://doc.rust-lang.org/beta/std/ops/trait.Try.html) type
/// supported, please open an issue or a PR.
///
/// [`Try`]: https://doc.rust-lang.org/beta/std/ops/trait.Try.html
/// [`Try::Output`]: https://doc.rust-lang.org/beta/std/ops/trait.Try.html#associatedtype.Output
#[macro_export]
macro_rules! try_map_both {
($value:expr, $pattern:pat => $result:expr) => {
match $value {
$crate::Either::Left($pattern) => {
let f = move || {
let result = $result?;
let e = $crate::Either::Left(result);
<_ as $crate::__either_internals::Tryish>::from_output(e)
};
f()
}
$crate::Either::Right($pattern) => {
let f = move || {
let result = $result?;
let e = $crate::Either::Right(result);
<_ as $crate::__either_internals::Tryish>::from_output(e)
};
f()
}
}
};
}

/// Macro for unwrapping the left side of an [`Either`], which fails early
/// with the opposite side. Can only be used in functions that return
/// `Either` because of the early return of `Right` that it provides.
Expand Down Expand Up @@ -130,15 +225,6 @@ macro_rules! try_right {
};
}

macro_rules! map_either {
($value:expr, $pattern:pat => $result:expr) => {
match $value {
Left($pattern) => Left($result),
Right($pattern) => Right($result),
}
};
}

mod iterator;
pub use self::iterator::IterEither;

Expand Down Expand Up @@ -539,7 +625,7 @@ impl<L, R> Either<L, R> {
L: IntoIterator,
R: IntoIterator<Item = L::Item>,
{
map_either!(self, inner => inner.into_iter())
map_both!(self, inner => inner.into_iter())
}

/// Borrow the inner value as an iterator.
Expand All @@ -562,7 +648,7 @@ impl<L, R> Either<L, R> {
for<'a> &'a L: IntoIterator,
for<'a> &'a R: IntoIterator<Item = <&'a L as IntoIterator>::Item>,
{
map_either!(self, inner => inner.into_iter())
map_both!(self, inner => inner.into_iter())
}

/// Mutably borrow the inner value as an iterator.
Expand Down Expand Up @@ -593,7 +679,7 @@ 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>,
{
map_either!(self, inner => inner.into_iter())
map_both!(self, inner => inner.into_iter())
}

/// Converts an `Either` of `Iterator`s to be an `Iterator` of `Either`s
Expand All @@ -617,7 +703,7 @@ impl<L, R> Either<L, R> {
L: IntoIterator,
R: IntoIterator,
{
IterEither::new(map_either!(self, inner => inner.into_iter()))
IterEither::new(map_both!(self, inner => inner.into_iter()))
}

/// Borrows an `Either` of `Iterator`s to be an `Iterator` of `Either`s
Expand All @@ -641,7 +727,7 @@ impl<L, R> Either<L, R> {
for<'a> &'a L: IntoIterator,
for<'a> &'a R: IntoIterator,
{
IterEither::new(map_either!(self, inner => inner.into_iter()))
IterEither::new(map_both!(self, inner => inner.into_iter()))
}

/// Mutably borrows an `Either` of `Iterator`s to be an `Iterator` of `Either`s
Expand All @@ -667,7 +753,7 @@ impl<L, R> Either<L, R> {
for<'a> &'a mut L: IntoIterator,
for<'a> &'a mut R: IntoIterator,
{
IterEither::new(map_either!(self, inner => inner.into_iter()))
IterEither::new(map_both!(self, inner => inner.into_iter()))
}

/// Return left value or given value
Expand Down Expand Up @@ -1573,3 +1659,44 @@ fn _unsized_std_propagation() {
check_t!(::std::ffi::OsStr);
check_t!(::std::ffi::CStr);
}

mod private {
/// The trait that allows [sealing] the hidden [`Tryish`](super::__either_internals::Tryish)
/// trait, which is used in [`try_map_both`] macro.
///
/// [sealing]: https://rust-lang.github.io/api-guidelines/future-proofing.html#sealed-traits-protect-against-downstream-implementations-c-sealed
pub trait TryishSealed {}
}

#[doc(hidden)]
pub mod __either_internals {
use super::private::TryishSealed;

/// The minimal interface for a type to be used with [`try_map_both`]
/// while [`Try`](https://doc.rust-lang.org/beta/std/ops/trait.Try.html)
/// trait is unstable.
pub trait Tryish: TryishSealed {
type Output;

fn from_output(output: Self::Output) -> Self;
}

impl<T, E> TryishSealed for Result<T, E> {}
impl<T> TryishSealed for Option<T> {}

impl<T> Tryish for Option<T> {
type Output = T;

fn from_output(output: Self::Output) -> Self {
Some(output)
}
}

impl<T, E> Tryish for Result<T, E> {
type Output = T;

fn from_output(output: Self::Output) -> Self {
Ok(output)
}
}
}