all-the-errors 0.1.0-beta3

Collect all the errors from iterators over results
Documentation
#![no_std]
//! This crate exports the [`CollectAllTheErrors`] trait and its implementation
//! for [`Iterator<Item=Result>`] objects.
//!
//! When in scope, this trait adds
//! the [`partition_results`](CollectAllTheErrors::partition_results),
//! [`collected_results`](CollectAllTheErrors::collected_results),
//! and (slightly more situational)
//! [`collect_oks_or_iter_errs`](CollectAllTheErrors::collect_oks_or_iter_errs)
//! methods to iterators over [`Result`]s.
use core::iter;

pub const VERSION: &str = env!("CARGO_PKG_VERSION");

/// Trait for getting _all_ the errors from an iterator over results.
///
/// Mostly useful for its [implementation on
/// `Iterator<Item=Result<T, E>>`](#impl-CollectAllTheErrors<T,+E>-for-Iter).
pub trait CollectAllTheErrors<T, E> {
    /// Partition results into success and err values.
    ///
    /// Unlike [`Iterator::partition`], which returns, for instance
    /// `(Vec<Result<T, E>>, Vec<Result<T, E>>)`,
    /// this can return `(Vec<T>, Vec<E>)`.
    fn partition_results<Oks, Errs>(self) -> (Oks, Errs)
    where
        Oks: Default + Extend<T>,
        Errs: Default + Extend<E>;

    /// Returns `Ok(all_oks)` if no error occurs, otherwise `Err(all_errs)`.
    ///
    /// Unlike [`Iterator::collect`], which might returns, for instance
    /// `Result<Vec<T>, E>`, this can return
    /// this can return `Result<Vec<T>, Vec<E>>`.
    fn collected_results<Oks, Errs>(self) -> Result<Oks, Errs>
    where
        Oks: Default + Extend<T>,
        Errs: FromIterator<E>;

    /// Returns `Ok(all_oks)` if no error occurs, otherwise `Err(iterator over all errors)`.
    fn collect_oks_or_iter_errs<Oks>(self) -> Result<Oks, impl Iterator<Item = E>>
    where
        Oks: Default + Extend<T>;
}

/// Primary implementation of the [`CollectAllTheErrors`] trait.
///
/// ## Example: collecting into `Vec`s
///
/// For clarity, let's assume you want to collect results into `Vec`s
/// (see the docs for the generic signature).
///
/// Bringing this trait into scope will give you the following
/// extra methods on `iter: Iterable<Item=Result<T, E>>`:
///
/// 1. `iter.partition_results() -> (Vec<T>, Vec<E>)`
/// 2. `iter.collected_results() -> Result<Vec<T>, Vec<E>>`
/// 3. `iter.collect_oks_or_iter_errs() -> Result<Vec<T>, impl Iterator<Item=E>>`
///
/// For example:
///
/// ```rust
/// use all_the_errors::CollectAllTheErrors;
///
/// assert_eq!(
///     [Ok(1), Err("a"), Ok(2), Err("b")]
///         .into_iter()
///         .partition_results(),
///     (vec![1, 2], vec!["a", "b"])
/// );
///
/// assert_eq!(
///     [Ok(1), Ok(2)]
///         .into_iter()
///         .collected_results::<Vec<_>, Vec<()>>(),
///     Ok(vec![1, 2])
/// );
///
/// assert_eq!(
///     [Ok(1), Err("a"), Ok(2), Err("b")]
///         .into_iter()
///         .collected_results::<Vec<_>, Vec<_>>(),
///     Err(vec!["a", "b"])
/// );
/// ```
impl<Iter, T, E> CollectAllTheErrors<T, E> for Iter
where
    Iter: Iterator<Item = Result<T, E>>,
{
    fn partition_results<Oks, Errs>(self) -> (Oks, Errs)
    where
        Oks: Default + Extend<T>,
        Errs: Default + Extend<E>,
    {
        let mut oks: Oks = Default::default();
        let mut errs: Errs = Default::default();

        for item in self {
            match item {
                Ok(ok) => oks.extend(Some(ok)),
                Err(err) => errs.extend(Some(err)),
            }
        }

        (oks, errs)
    }

    // Q: if we need to switch from collecting successes to failures,
    //    could we clear() the Vec<T> then reuse it? (with unsafe, obvs)
    // A: NO - if you used `into/from_raw_parts` to recast
    //    the pointer, it would be UB if T and E have different alignments.
    //    And even if you could guarantee compatible alignments,
    //    you'd need to fix the capacity of the allocation to be an
    //    integer multiple of E's size.
    //    See https://doc.rust-lang.org/std/vec/struct.Vec.html#method.from_raw_parts
    //
    // As an alternative, we just drop the first vec before filling the second one,
    // which *may* encourage the allocator to reuse the memory rather than
    // making a syscall? <- complete untested conjecture

    fn collected_results<Oks, Errs>(self) -> Result<Oks, Errs>
    where
        Oks: Default + Extend<T>,
        Errs: FromIterator<E>,
    {
        self.collect_oks_or_iter_errs()
            .map_err(|e| e.collect::<Errs>())
    }

    fn collect_oks_or_iter_errs<Oks>(mut self) -> Result<Oks, impl Iterator<Item = E>>
    where
        Oks: Default + Extend<T>,
    {
        // collect successes until iterator ends or we find an error
        let first_err: E;
        let mut successes: Oks = Default::default();
        loop {
            match self.next() {
                None => return Ok(successes),
                Some(Ok(succ)) => successes.extend(Some(succ)),
                Some(Err(err)) => {
                    first_err = err;
                    break;
                }
            }
        }

        Err(iter::once(first_err).chain(self.filter_map(Result::err)))
    }
}