206 lines
6.0 KiB
Rust
206 lines
6.0 KiB
Rust
use crate::size_hint;
|
|
use std::{
|
|
fmt,
|
|
iter::{DoubleEndedIterator, FusedIterator},
|
|
};
|
|
|
|
pub fn flatten_ok<I, T, E>(iter: I) -> FlattenOk<I, T, E>
|
|
where
|
|
I: Iterator<Item = Result<T, E>>,
|
|
T: IntoIterator,
|
|
{
|
|
FlattenOk {
|
|
iter,
|
|
inner_front: None,
|
|
inner_back: None,
|
|
}
|
|
}
|
|
|
|
/// An iterator adaptor that flattens `Result::Ok` values and
|
|
/// allows `Result::Err` values through unchanged.
|
|
///
|
|
/// See [`.flatten_ok()`](crate::Itertools::flatten_ok) for more information.
|
|
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
|
|
pub struct FlattenOk<I, T, E>
|
|
where
|
|
I: Iterator<Item = Result<T, E>>,
|
|
T: IntoIterator,
|
|
{
|
|
iter: I,
|
|
inner_front: Option<T::IntoIter>,
|
|
inner_back: Option<T::IntoIter>,
|
|
}
|
|
|
|
impl<I, T, E> Iterator for FlattenOk<I, T, E>
|
|
where
|
|
I: Iterator<Item = Result<T, E>>,
|
|
T: IntoIterator,
|
|
{
|
|
type Item = Result<T::Item, E>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
loop {
|
|
// Handle the front inner iterator.
|
|
if let Some(inner) = &mut self.inner_front {
|
|
if let Some(item) = inner.next() {
|
|
return Some(Ok(item));
|
|
}
|
|
|
|
// This is necessary for the iterator to implement `FusedIterator`
|
|
// with only the original iterator being fused.
|
|
self.inner_front = None;
|
|
}
|
|
|
|
match self.iter.next() {
|
|
Some(Ok(ok)) => self.inner_front = Some(ok.into_iter()),
|
|
Some(Err(e)) => return Some(Err(e)),
|
|
None => {
|
|
// Handle the back inner iterator.
|
|
if let Some(inner) = &mut self.inner_back {
|
|
if let Some(item) = inner.next() {
|
|
return Some(Ok(item));
|
|
}
|
|
|
|
// This is necessary for the iterator to implement `FusedIterator`
|
|
// with only the original iterator being fused.
|
|
self.inner_back = None;
|
|
} else {
|
|
return None;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn fold<B, F>(self, init: B, mut f: F) -> B
|
|
where
|
|
Self: Sized,
|
|
F: FnMut(B, Self::Item) -> B,
|
|
{
|
|
// Front
|
|
let mut acc = match self.inner_front {
|
|
Some(x) => x.fold(init, |a, o| f(a, Ok(o))),
|
|
None => init,
|
|
};
|
|
|
|
acc = self.iter.fold(acc, |acc, x| match x {
|
|
Ok(it) => it.into_iter().fold(acc, |a, o| f(a, Ok(o))),
|
|
Err(e) => f(acc, Err(e)),
|
|
});
|
|
|
|
// Back
|
|
match self.inner_back {
|
|
Some(x) => x.fold(acc, |a, o| f(a, Ok(o))),
|
|
None => acc,
|
|
}
|
|
}
|
|
|
|
fn size_hint(&self) -> (usize, Option<usize>) {
|
|
let inner_hint = |inner: &Option<T::IntoIter>| {
|
|
inner
|
|
.as_ref()
|
|
.map(Iterator::size_hint)
|
|
.unwrap_or((0, Some(0)))
|
|
};
|
|
let inner_front = inner_hint(&self.inner_front);
|
|
let inner_back = inner_hint(&self.inner_back);
|
|
// The outer iterator `Ok` case could be (0, None) as we don't know its size_hint yet.
|
|
let outer = match self.iter.size_hint() {
|
|
(0, Some(0)) => (0, Some(0)),
|
|
_ => (0, None),
|
|
};
|
|
|
|
size_hint::add(size_hint::add(inner_front, inner_back), outer)
|
|
}
|
|
}
|
|
|
|
impl<I, T, E> DoubleEndedIterator for FlattenOk<I, T, E>
|
|
where
|
|
I: DoubleEndedIterator<Item = Result<T, E>>,
|
|
T: IntoIterator,
|
|
T::IntoIter: DoubleEndedIterator,
|
|
{
|
|
fn next_back(&mut self) -> Option<Self::Item> {
|
|
loop {
|
|
// Handle the back inner iterator.
|
|
if let Some(inner) = &mut self.inner_back {
|
|
if let Some(item) = inner.next_back() {
|
|
return Some(Ok(item));
|
|
}
|
|
|
|
// This is necessary for the iterator to implement `FusedIterator`
|
|
// with only the original iterator being fused.
|
|
self.inner_back = None;
|
|
}
|
|
|
|
match self.iter.next_back() {
|
|
Some(Ok(ok)) => self.inner_back = Some(ok.into_iter()),
|
|
Some(Err(e)) => return Some(Err(e)),
|
|
None => {
|
|
// Handle the front inner iterator.
|
|
if let Some(inner) = &mut self.inner_front {
|
|
if let Some(item) = inner.next_back() {
|
|
return Some(Ok(item));
|
|
}
|
|
|
|
// This is necessary for the iterator to implement `FusedIterator`
|
|
// with only the original iterator being fused.
|
|
self.inner_front = None;
|
|
} else {
|
|
return None;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn rfold<B, F>(self, init: B, mut f: F) -> B
|
|
where
|
|
Self: Sized,
|
|
F: FnMut(B, Self::Item) -> B,
|
|
{
|
|
// Back
|
|
let mut acc = match self.inner_back {
|
|
Some(x) => x.rfold(init, |a, o| f(a, Ok(o))),
|
|
None => init,
|
|
};
|
|
|
|
acc = self.iter.rfold(acc, |acc, x| match x {
|
|
Ok(it) => it.into_iter().rfold(acc, |a, o| f(a, Ok(o))),
|
|
Err(e) => f(acc, Err(e)),
|
|
});
|
|
|
|
// Front
|
|
match self.inner_front {
|
|
Some(x) => x.rfold(acc, |a, o| f(a, Ok(o))),
|
|
None => acc,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<I, T, E> Clone for FlattenOk<I, T, E>
|
|
where
|
|
I: Iterator<Item = Result<T, E>> + Clone,
|
|
T: IntoIterator,
|
|
T::IntoIter: Clone,
|
|
{
|
|
clone_fields!(iter, inner_front, inner_back);
|
|
}
|
|
|
|
impl<I, T, E> fmt::Debug for FlattenOk<I, T, E>
|
|
where
|
|
I: Iterator<Item = Result<T, E>> + fmt::Debug,
|
|
T: IntoIterator,
|
|
T::IntoIter: fmt::Debug,
|
|
{
|
|
debug_fmt_fields!(FlattenOk, iter, inner_front, inner_back);
|
|
}
|
|
|
|
/// Only the iterator being flattened needs to implement [`FusedIterator`].
|
|
impl<I, T, E> FusedIterator for FlattenOk<I, T, E>
|
|
where
|
|
I: FusedIterator<Item = Result<T, E>>,
|
|
T: IntoIterator,
|
|
{
|
|
}
|