Skip to content

Commit 80046c6

Browse files
committed
add .flatten_ok()
1 parent 853f064 commit 80046c6

File tree

2 files changed

+87
-0
lines changed

2 files changed

+87
-0
lines changed

src/flatten_ok.rs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
pub fn flatten_ok<I, T, E>(iter: I) -> FlattenOk<I, T, E>
2+
where
3+
I: Iterator<Item = Result<T, E>>,
4+
T: IntoIterator,
5+
{
6+
FlattenOk { iter, inner: None }
7+
}
8+
9+
/// An iterator adaptor that flattens `Result::Ok` values and
10+
/// allows `Result::Err` values through unchanged.
11+
///
12+
/// See [`.flatten_ok()`](crate::Itertools::flatten_ok) for more information.
13+
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
14+
pub struct FlattenOk<I, T, E>
15+
where
16+
I: Iterator<Item = Result<T, E>>,
17+
T: IntoIterator,
18+
{
19+
iter: I,
20+
inner: Option<T::IntoIter>,
21+
}
22+
23+
impl<I, T, E> Iterator for FlattenOk<I, T, E>
24+
where
25+
I: Iterator<Item = Result<T, E>>,
26+
T: IntoIterator,
27+
{
28+
type Item = Result<T::Item, E>;
29+
30+
fn next(&mut self) -> Option<Self::Item> {
31+
loop {
32+
if let Some(inner) = &mut self.inner {
33+
if let Some(item) = inner.next() {
34+
return Some(Ok(item));
35+
} else {
36+
self.inner = None;
37+
}
38+
} else {
39+
match self.iter.next() {
40+
Some(Ok(ok)) => self.inner = Some(ok.into_iter()),
41+
Some(Err(e)) => return Some(Err(e)),
42+
None => return None,
43+
}
44+
}
45+
}
46+
}
47+
}
48+
49+
impl<I, T, E> Clone for FlattenOk<I, T, E>
50+
where
51+
I: Iterator<Item = Result<T, E>> + Clone,
52+
T: IntoIterator,
53+
T::IntoIter: Clone,
54+
{
55+
fn clone(&self) -> Self {
56+
Self {
57+
iter: self.iter.clone(),
58+
inner: self.inner.clone(),
59+
}
60+
}
61+
}

src/lib.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ pub mod structs {
119119
pub use crate::cons_tuples_impl::ConsTuples;
120120
pub use crate::exactly_one_err::ExactlyOneError;
121121
pub use crate::format::{Format, FormatWith};
122+
pub use crate::flatten_ok::FlattenOk;
122123
#[cfg(feature = "use_std")]
123124
pub use crate::grouping_map::{GroupingMap, GroupingMapBy};
124125
#[cfg(feature = "use_alloc")]
@@ -194,6 +195,7 @@ mod combinations;
194195
mod combinations_with_replacement;
195196
mod exactly_one_err;
196197
mod diff;
198+
mod flatten_ok;
197199
mod format;
198200
#[cfg(feature = "use_std")]
199201
mod grouping_map;
@@ -833,6 +835,30 @@ pub trait Itertools : Iterator {
833835
adaptors::filter_map_ok(self, f)
834836
}
835837

838+
/// Return an iterator adaptor that flattens every `Result::Ok` value into
839+
/// a series of `Result::Ok` values. `Result::Err` values are unchanged.
840+
///
841+
/// This is useful when you have some common error type for your crate and
842+
/// need to propogate it upwards, but the `Result::Ok` case needs to be flattened.
843+
///
844+
/// ```
845+
/// use itertools::Itertools;
846+
///
847+
/// let input = vec![Ok(0..2), Err(false), Ok(2..4)];
848+
/// let it = input.iter().cloned().flatten_ok();
849+
/// itertools::assert_equal(it.clone(), vec![Ok(0), Ok(1), Err(false), Ok(2), Ok(3)]);
850+
///
851+
/// // This can also be used to propogate errors when collecting.
852+
/// let output_result: Result<Vec<i32>, bool> = it.collect();
853+
/// assert_eq!(output_result, Err(false));
854+
/// ```
855+
fn flatten_ok<T, E>(self) -> FlattenOk<Self, T, E>
856+
where Self: Iterator<Item = Result<T, E>> + Sized,
857+
T: IntoIterator
858+
{
859+
flatten_ok::flatten_ok(self)
860+
}
861+
836862
/// Return an iterator adaptor that merges the two base iterators in
837863
/// ascending order. If both base iterators are sorted (ascending), the
838864
/// result is sorted.

0 commit comments

Comments
 (0)