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

Add map_err and err_into iterator adapters #714

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
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
Next Next commit
Add map_err
  • Loading branch information
khollbach committed Jul 23, 2023
commit 044ddda7f94cb4cd14ecc74fb7900a16ff9990e9
35 changes: 35 additions & 0 deletions src/adaptors/map.rs
Original file line number Diff line number Diff line change
@@ -122,3 +122,38 @@ pub fn map_into<I, R>(iter: I) -> MapInto<I, R> {
f: MapSpecialCaseFnInto(PhantomData),
}
}

/// An iterator adapter to apply a transformation within a nested `Result::Err`.
///
/// See [`.map_err()`](crate::Itertools::map_err) for more information.
pub type MapErr<I, F> = MapSpecialCase<I, MapSpecialCaseFnErr<F>>;

/// Create a new `MapErr` iterator.
pub(crate) fn map_err<I, F, T, E, E2>(iter: I, f: F) -> MapErr<I, F>
where
I: Iterator<Item = Result<T, E>>,
F: FnMut(E) -> E2,
{
MapSpecialCase {
iter,
f: MapSpecialCaseFnErr(f),
}
}

#[derive(Clone)]
pub struct MapSpecialCaseFnErr<F>(F);

impl<F> std::fmt::Debug for MapSpecialCaseFnErr<F> {
debug_fmt_fields!(MapSpecialCaseFnErr,);
}

impl<F, T, E, E2> MapSpecialCaseFn<Result<T, E>> for MapSpecialCaseFnErr<F>
where
F: FnMut(E) -> E2,
{
type Out = Result<T, E2>;

fn call(&mut self, r: Result<T, E>) -> Self::Out {
r.map_err(|v| self.0(v))
}
}
5 changes: 4 additions & 1 deletion src/adaptors/mod.rs
Original file line number Diff line number Diff line change
@@ -7,13 +7,16 @@
mod coalesce;
mod map;
mod multi_product;

pub use self::coalesce::*;
pub use self::map::{map_into, map_ok, MapInto, MapOk};
pub use self::map::{map_into, map_ok, MapInto, MapOk, MapErr};
#[allow(deprecated)]
pub use self::map::MapResults;
#[cfg(feature = "use_alloc")]
pub use self::multi_product::*;

pub(crate) use self::map::map_err;

use std::fmt;
use std::iter::{Fuse, Peekable, FromIterator, FusedIterator};
use std::marker::PhantomData;
21 changes: 21 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -99,6 +99,7 @@ pub mod structs {
Batching,
MapInto,
MapOk,
MapErr,
Merge,
MergeBy,
TakeWhileRef,
@@ -928,6 +929,26 @@ pub trait Itertools : Iterator {
flatten_ok::flatten_ok(self)
}

/// Return an iterator adaptor that applies the provided closure to every
/// [`Result::Err`] value. [`Result::Ok`] values are unchanged.
///
/// # Examples
///
/// ```
/// use itertools::Itertools;
///
/// let iterator = vec![Ok(41), Err(0), Ok(11)].into_iter();
/// let mapped = iterator.map_err(|x| x + 2);
/// itertools::assert_equal(mapped, vec![Ok(41), Err(2), Ok(11)]);
/// ```
fn map_err<F, T, E, E2>(self, f: F) -> MapErr<Self, F>
where
Self: Iterator<Item = Result<T, E>> + Sized,
F: FnMut(E) -> E2,
{
adaptors::map_err(self, f)
}

/// “Lift” a function of the values of the current iterator so as to process
/// an iterator of `Result` values instead.
///
6 changes: 6 additions & 0 deletions tests/specializations.rs
Original file line number Diff line number Diff line change
@@ -105,6 +105,12 @@ quickcheck! {
}
}

quickcheck! {
fn map_err(v: Vec<Result<char, u8>>) -> () {
test_specializations(&v.into_iter().map_err(|u| u.checked_add(1)));
}
}

quickcheck! {
fn process_results(v: Vec<Result<u8, u8>>) -> () {
helper(v.iter().copied());