diff --git a/src/adaptors/mod.rs b/src/adaptors/mod.rs index 038fa6951..a0fd61f9c 100644 --- a/src/adaptors/mod.rs +++ b/src/adaptors/mod.rs @@ -1260,6 +1260,56 @@ impl Iterator for FilterMapOk }).collect() } } + +/// An iterator adapter to apply a fallible transformation within a nested `Result`. +/// +/// See [`.map_and_then()`](../trait.Itertools.html#method.map_and_then) for more information. +#[derive(Clone)] +#[must_use = "iterator adaptors are lazy and do nothing unless consumed"] +pub struct MapAndThen { + iter: I, + f: F +} + +/// Create a new `MapAndThen` iterator. +pub fn map_and_then(iter: I, f: F) -> MapAndThen + where I: Iterator>, + F: FnMut(T) -> Result +{ + MapAndThen { + iter: iter, + f: f, + } +} + +impl Iterator for MapAndThen + where I: Iterator>, + F: FnMut(T) -> Result +{ + type Item = Result; + + fn next(&mut self) -> Option { + self.iter.next().map(|v| v.and_then(&mut self.f)) + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } + + fn fold(self, init: Acc, mut fold_f: Fold) -> Acc + where Fold: FnMut(Acc, Self::Item) -> Acc, + { + let mut f = self.f; + self.iter.fold(init, move |acc, v| fold_f(acc, v.and_then(&mut f))) + } + + fn collect(self) -> C + where C: FromIterator + { + let mut f = self.f; + self.iter.map(move |v| v.and_then(&mut f)).collect() + } +} /// An iterator adapter to get the positions of each element that matches a predicate. /// diff --git a/src/lib.rs b/src/lib.rs index 411d570d9..6ccbce096 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,7 @@ pub mod structs { Batching, MapInto, MapOk, + MapAndThen, Merge, MergeBy, TakeWhileRef, @@ -811,6 +812,24 @@ pub trait Itertools : Iterator { adaptors::filter_map_ok(self, f) } + /// Return an iterator adaptor that applies the provided fallible + /// closure to every `Result::Ok` value. `Result::Err` values in + /// the original iterator are unchanged. + /// + /// ``` + /// use itertools::Itertools; + /// + /// let input = vec![Ok(41), Err(false), Ok(i32::max_value())]; + /// let it = input.into_iter().map_and_then(|i| i.checked_add(1).ok_or(true)); + /// itertools::assert_equal(it, vec![Ok(42), Err(false), Err(true)]); + /// ``` + fn map_and_then(self, f: F) -> MapAndThen + where Self: Iterator> + Sized, + F: FnMut(T) -> Result, + { + adaptors::map_and_then(self, f) + } + /// Return an iterator adaptor that merges the two base iterators in /// ascending order. If both base iterators are sorted (ascending), the /// result is sorted.