all_the_errors/lib.rs
1#![no_std]
2//! This crate exports the [`CollectAllTheErrors`] trait and its implementation
3//! for [`Iterator<Item=Result>`] objects.
4//!
5//! When in scope, this trait adds
6//! the [`partition_results`](CollectAllTheErrors::partition_results),
7//! [`collected_results`](CollectAllTheErrors::collected_results),
8//! and (slightly more situational)
9//! [`collect_oks_or_iter_errs`](CollectAllTheErrors::collect_oks_or_iter_errs)
10//! methods to iterators over [`Result`]s.
11use core::iter;
12
13pub const VERSION: &str = env!("CARGO_PKG_VERSION");
14
15/// Trait for getting _all_ the errors from an iterator over results.
16///
17/// Mostly useful for its [implementation on
18/// `Iterator<Item=Result<T, E>>`](#impl-CollectAllTheErrors<T,+E>-for-Iter).
19pub trait CollectAllTheErrors<T, E> {
20 /// Partition results into success and err values.
21 ///
22 /// Unlike [`Iterator::partition`], which returns, for instance
23 /// `(Vec<Result<T, E>>, Vec<Result<T, E>>)`,
24 /// this can return `(Vec<T>, Vec<E>)`.
25 fn partition_results<Oks, Errs>(self) -> (Oks, Errs)
26 where
27 Oks: Default + Extend<T>,
28 Errs: Default + Extend<E>;
29
30 /// Returns `Ok(all_oks)` if no error occurs, otherwise `Err(all_errs)`.
31 ///
32 /// Unlike [`Iterator::collect`], which might returns, for instance
33 /// `Result<Vec<T>, E>`, this can return
34 /// this can return `Result<Vec<T>, Vec<E>>`.
35 fn collected_results<Oks, Errs>(self) -> Result<Oks, Errs>
36 where
37 Oks: Default + Extend<T>,
38 Errs: FromIterator<E>;
39
40 /// Returns `Ok(all_oks)` if no error occurs, otherwise `Err(iterator over all errors)`.
41 fn collect_oks_or_iter_errs<Oks>(self) -> Result<Oks, impl Iterator<Item = E>>
42 where
43 Oks: Default + Extend<T>;
44}
45
46/// Primary implementation of the [`CollectAllTheErrors`] trait.
47///
48/// ## Example: collecting into `Vec`s
49///
50/// For clarity, let's assume you want to collect results into `Vec`s
51/// (see the docs for the generic signature).
52///
53/// Bringing this trait into scope will give you the following
54/// extra methods on `iter: Iterable<Item=Result<T, E>>`:
55///
56/// 1. `iter.partition_results() -> (Vec<T>, Vec<E>)`
57/// 2. `iter.collected_results() -> Result<Vec<T>, Vec<E>>`
58/// 3. `iter.collect_oks_or_iter_errs() -> Result<Vec<T>, impl Iterator<Item=E>>`
59///
60/// For example:
61///
62/// ```rust
63/// use all_the_errors::CollectAllTheErrors;
64///
65/// assert_eq!(
66/// [Ok(1), Err("a"), Ok(2), Err("b")]
67/// .into_iter()
68/// .partition_results(),
69/// (vec![1, 2], vec!["a", "b"])
70/// );
71///
72/// assert_eq!(
73/// [Ok(1), Ok(2)]
74/// .into_iter()
75/// .collected_results::<Vec<_>, Vec<()>>(),
76/// Ok(vec![1, 2])
77/// );
78///
79/// assert_eq!(
80/// [Ok(1), Err("a"), Ok(2), Err("b")]
81/// .into_iter()
82/// .collected_results::<Vec<_>, Vec<_>>(),
83/// Err(vec!["a", "b"])
84/// );
85/// ```
86impl<Iter, T, E> CollectAllTheErrors<T, E> for Iter
87where
88 Iter: Iterator<Item = Result<T, E>>,
89{
90 fn partition_results<Oks, Errs>(self) -> (Oks, Errs)
91 where
92 Oks: Default + Extend<T>,
93 Errs: Default + Extend<E>,
94 {
95 let mut oks: Oks = Default::default();
96 let mut errs: Errs = Default::default();
97
98 for item in self {
99 match item {
100 Ok(ok) => oks.extend(Some(ok)),
101 Err(err) => errs.extend(Some(err)),
102 }
103 }
104
105 (oks, errs)
106 }
107
108 // Q: if we need to switch from collecting successes to failures,
109 // could we clear() the Vec<T> then reuse it? (with unsafe, obvs)
110 // A: NO - if you used `into/from_raw_parts` to recast
111 // the pointer, it would be UB if T and E have different alignments.
112 // And even if you could guarantee compatible alignments,
113 // you'd need to fix the capacity of the allocation to be an
114 // integer multiple of E's size.
115 // See https://doc.rust-lang.org/std/vec/struct.Vec.html#method.from_raw_parts
116 //
117 // As an alternative, we just drop the first vec before filling the second one,
118 // which *may* encourage the allocator to reuse the memory rather than
119 // making a syscall? <- complete untested conjecture
120
121 fn collected_results<Oks, Errs>(self) -> Result<Oks, Errs>
122 where
123 Oks: Default + Extend<T>,
124 Errs: FromIterator<E>,
125 {
126 self.collect_oks_or_iter_errs()
127 .map_err(|e| e.collect::<Errs>())
128 }
129
130 fn collect_oks_or_iter_errs<Oks>(mut self) -> Result<Oks, impl Iterator<Item = E>>
131 where
132 Oks: Default + Extend<T>,
133 {
134 // collect successes until iterator ends or we find an error
135 let first_err: E;
136 let mut successes: Oks = Default::default();
137 loop {
138 match self.next() {
139 None => return Ok(successes),
140 Some(Ok(succ)) => successes.extend(Some(succ)),
141 Some(Err(err)) => {
142 first_err = err;
143 break;
144 }
145 }
146 }
147
148 Err(iter::once(first_err).chain(self.filter_map(Result::err)))
149 }
150}