use alloc::rc::Rc;
use core::cell::RefCell;
use tap::Pipe;
use crate::TryFromIterator;
use crate::errors::ResultCollectError;
use crate::errors::types::SizeHint;
#[subdef::subdef(derive(Debug))]
pub struct ResultIter<I: Iterator, E>(
[Rc<RefCell<IterState<I, E>>>; {
enum IterState<Iter, E> {
Active(Iter),
Taken,
Errored { error: E, remaining: Iter },
}
}],
);
impl<I, E, T> ResultIter<I, E>
where
I: Iterator<Item = Result<T, E>>,
{
const EMPTY_STATE: IterState<I, E> = IterState::Taken;
fn new(iter: I) -> Self {
IterState::Active(iter).pipe(RefCell::new).pipe(Rc::new).pipe(ResultIter)
}
fn take_inner(&self) -> IterState<I, E> {
core::mem::replace(&mut *self.0.borrow_mut(), Self::EMPTY_STATE)
}
fn share(&self) -> Self {
self.0.clone().pipe(ResultIter)
}
}
impl<Iter: Iterator, E> IterState<Iter, E> {
fn advance<T>(self) -> (Self, Option<T>)
where
Iter: Iterator<Item = Result<T, E>>,
{
match self {
Self::Active(mut iter) => match iter.next() {
Some(Ok(v)) => (Self::Active(iter), Some(v)),
Some(Err(e)) => (Self::Errored { error: e, remaining: iter }, None),
None => (Self::Active(iter), None),
},
state @ (Self::Errored { .. } | Self::Taken) => (state, None),
}
}
}
impl<I, T, E> Iterator for ResultIter<I, E>
where
I: Iterator<Item = Result<T, E>>,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
let mut state = self.0.borrow_mut();
let (new_state, item) = core::mem::replace(&mut *state, Self::EMPTY_STATE).advance();
*state = new_state;
item
}
fn size_hint(&self) -> (usize, Option<usize>) {
match &*self.0.borrow() {
IterState::Errored { .. } | IterState::Taken => SizeHint::ZERO.into(),
IterState::Active(iter) => (0, iter.size_hint().1),
}
}
}
impl<I, T, E, C> TryFromIterator<I> for Result<C, C::Error>
where
I: IntoIterator<Item = Result<T, E>>,
C: TryFromIterator<ResultIter<I::IntoIter, E>>,
{
type Error = ResultCollectError<E, C, C::Error, I::IntoIter>;
fn try_from_iter(into_iter: I) -> Result<Self, Self::Error> {
let extractor = ResultIter::new(into_iter.into_iter());
let try_from_result = extractor.share().pipe(C::try_from_iter);
match (extractor.take_inner(), try_from_result) {
(IterState::Active(_), Ok(v)) => Ok(Ok(v)),
(IterState::Active(_), Err(e)) => Ok(Err(e)),
(IterState::Errored { error, remaining }, result) => Err(ResultCollectError::new(error, result, remaining)),
(IterState::Taken, _) => unreachable!("take_inner called multiple times"),
}
}
}
#[cfg(test)]
mod result_iter {
use super::*;
const TEST_DATA: [Result<i32, i32>; 4] = [Ok(1), Err(3), Ok(3), Ok(4)];
#[test]
fn zero_size_after_err() {
let mut extractor = ResultIter::new(TEST_DATA.into_iter());
extractor.next(); extractor.next(); extractor.next();
assert_eq!(extractor.size_hint(), (0, Some(0)));
}
#[test]
fn extract_err_forward_hint() {
let mut extractor = ResultIter::new(TEST_DATA.into_iter());
extractor.next();
assert_eq!(extractor.size_hint(), (0, Some(3)));
}
}
#[cfg(test)]
mod iter_state_advance {
use super::*;
macro_rules! test {
($name:ident: $state:expr => value: $expected_value:expr, state: $expected_state:pat) => {
#[test]
fn $name() {
let state = $state;
let (new_state, value) = state.advance();
assert_eq!(value, $expected_value);
assert!(matches!(new_state, $expected_state));
}
};
}
test!(advance_active_yields_ok:
IterState::Active([Ok::<_, &str>(1)].into_iter())
=> value: Some(1), state: IterState::Active(_)
);
test!(advance_active_yields_err:
IterState::Active([Err::<i32, &str>("error")].into_iter())
=> value: None, state: IterState::Errored { error: "error", .. }
);
test!(advance_active_exhausted:
IterState::Active(core::iter::empty::<Result<i32, &str>>())
=> value: None, state: IterState::Active(_)
);
test!(advance_errored_stays_errored:
IterState::Errored { error: "error", remaining: [Ok(1), Ok(2)].into_iter() }
=> value: None, state: IterState::Errored { error: "error", .. }
);
test!(advance_taken_stays_taken:
IterState::<std::vec::IntoIter<Result<i32, &str>>, &str>::Taken
=> value: None, state: IterState::Taken
);
}