Skip to content

Commit

Permalink
Merge pull request #82 from nasadorian/add-map-either
Browse files Browse the repository at this point in the history
Add map_either and map_either_with methods
  • Loading branch information
cuviper committed Jul 22, 2023
2 parents b0d9a95 + 261c527 commit 7a60ae8
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 1 deletion.
9 changes: 9 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ jobs:
with:
toolchain: ${{ matrix.rust }}

- name: MSRV dependencies
if: matrix.rust == '1.36.0'
run: |
cargo generate-lockfile
cargo update -p serde_json --precise 1.0.99
cargo update -p serde --precise 1.0.156
cargo update -p quote --precise 1.0.30
cargo update -p proc-macro2 --precise 1.0.65
- name: Build (no_std)
run: cargo build --no-default-features

Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "either"
version = "1.8.1"
version = "1.9.0"
authors = ["bluss"]
edition = "2018"
rust-version = "1.36"
Expand Down
4 changes: 4 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ How to use with cargo::
Recent Changes
--------------

- 1.9.0

- Add new methods ``.map_either()`` and ``.map_either_with()``, by @nasadorian (#82)

- 1.8.1

- Clarified that the multiple licenses are combined with OR.
Expand Down
59 changes: 59 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,65 @@ impl<L, R> Either<L, R> {
}
}

/// Apply the functions `f` and `g` to the `Left` and `Right` variants
/// respectively. This is equivalent to
/// [bimap](https://hackage.haskell.org/package/bifunctors-5/docs/Data-Bifunctor.html)
/// in functional programming.
///
/// ```
/// use either::*;
///
/// let f = |s: String| s.len();
/// let g = |u: u8| u.to_string();
///
/// let left: Either<String, u8> = Left("loopy".into());
/// assert_eq!(left.map_either(f, g), Left(5));
///
/// let right: Either<String, u8> = Right(42);
/// assert_eq!(right.map_either(f, g), Right("42".into()));
/// ```
pub fn map_either<F, G, M, S>(self, f: F, g: G) -> Either<M, S>
where
F: FnOnce(L) -> M,
G: FnOnce(R) -> S,
{
match self {
Left(l) => Left(f(l)),
Right(r) => Right(g(r)),
}
}

/// Similar to [`map_either`], with an added context `ctx` accessible to
/// both functions.
///
/// ```
/// use either::*;
///
/// let mut sum = 0;
///
/// // Both closures want to update the same value, so pass it as context.
/// let mut f = |sum: &mut usize, s: String| { *sum += s.len(); s.to_uppercase() };
/// let mut g = |sum: &mut usize, u: usize| { *sum += u; u.to_string() };
///
/// let left: Either<String, usize> = Left("loopy".into());
/// assert_eq!(left.map_either_with(&mut sum, &mut f, &mut g), Left("LOOPY".into()));
///
/// let right: Either<String, usize> = Right(42);
/// assert_eq!(right.map_either_with(&mut sum, &mut f, &mut g), Right("42".into()));
///
/// assert_eq!(sum, 47);
/// ```
pub fn map_either_with<Ctx, F, G, M, S>(self, ctx: Ctx, f: F, g: G) -> Either<M, S>
where
F: FnOnce(Ctx, L) -> M,
G: FnOnce(Ctx, R) -> S,
{
match self {
Left(l) => Left(f(ctx, l)),
Right(r) => Right(g(ctx, r)),
}
}

/// Apply one of two functions depending on contents, unifying their result. If the value is
/// `Left(L)` then the first function `f` is applied; if it is `Right(R)` then the second
/// function `g` is applied.
Expand Down

0 comments on commit 7a60ae8

Please sign in to comment.