use alloc::vec::Vec;
use core::{iter::FusedIterator, mem};
use crate::{IntoReport, report_collection::ReportCollection};
pub trait IteratorExt<A, E>: Sized + Iterator<Item = Result<A, E>> {
#[track_caller]
fn collect_reports<Container, ThreadSafety>(
self,
) -> Result<Container, ReportCollection<E::Context, ThreadSafety>>
where
Container: FromIterator<A>,
E: IntoReport<ThreadSafety>;
#[track_caller]
fn collect_reports_vec<ThreadSafety>(
self,
) -> Result<Vec<A>, ReportCollection<E::Context, ThreadSafety>>
where
E: IntoReport<ThreadSafety>;
}
struct IteratorWrapper<'a, Iter, Error, ThreadSafety: 'static>
where
Error: IntoReport<ThreadSafety>,
{
iter: Iter,
error_collection: &'a mut Option<ReportCollection<Error::Context, ThreadSafety>>,
}
impl<'a, Iter, ThreadSafety, Object, Error> Iterator
for IteratorWrapper<'a, Iter, Error, ThreadSafety>
where
Iter: Iterator<Item = Result<Object, Error>>,
Error: IntoReport<ThreadSafety>,
{
type Item = Object;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
if self.error_collection.is_some() {
return None;
}
match self.iter.next() {
Some(Ok(object)) => Some(object),
Some(Err(err)) => {
*self.error_collection = Some(ReportCollection::from_iter(
core::iter::once(err)
.chain((&mut self.iter).filter_map(|v| v.err()))
.map(|err| err.into_report().into_cloneable()),
));
None
}
None => None,
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
if self.error_collection.is_some() {
(0, Some(0))
} else {
let (_, upper) = self.iter.size_hint();
(0, upper)
}
}
}
impl<'a, Iter, ThreadSafety, Object, Error> FusedIterator
for IteratorWrapper<'a, Iter, Error, ThreadSafety>
where
Iter: FusedIterator<Item = Result<Object, Error>>,
Error: IntoReport<ThreadSafety>,
{
}
impl<A, E, I> IteratorExt<A, E> for I
where
I: Iterator<Item = Result<A, E>>,
{
#[inline]
fn collect_reports<Container, ThreadSafety>(
self,
) -> Result<Container, ReportCollection<E::Context, ThreadSafety>>
where
Container: FromIterator<A>,
E: IntoReport<ThreadSafety>,
{
let mut error_collection = None;
let result = Container::from_iter(IteratorWrapper {
iter: self,
error_collection: &mut error_collection,
});
if let Some(error_collection) = error_collection {
Err(error_collection)
} else {
Ok(result)
}
}
#[inline]
fn collect_reports_vec<ThreadSafety>(
mut self,
) -> Result<Vec<A>, ReportCollection<E::Context, ThreadSafety>>
where
E: IntoReport<ThreadSafety>,
{
let mut out = Vec::new();
while let Some(v) = self.next() {
match v {
Ok(v) => out.push(v),
Err(err) => {
mem::drop(out);
let mut collection = ReportCollection::new();
collection.push(err.into_report().into_cloneable());
collection.extend(
self.filter_map(|v| v.err())
.map(|e| e.into_report().into_cloneable()),
);
return Err(collection);
}
}
}
Ok(out)
}
}