fused_error/
iter.rs

1//! Extending iterator functionality with [`IteratorExt`].
2
3use crate::{accumulator::Accumulator, IntoResultParts};
4
5use std::iter::FusedIterator;
6
7/// An iterator that takes an input of `Result<T, IE>` items and accumulates the
8/// errors into an accumulator of error type `E`, yielding an output stream of
9/// `T` items. Errors of `IE` must implement [`Into<E>`].
10///
11/// This `struct` is created by the [`accumulate`](IteratorExt::accumulate)
12/// method on [`Iterator`] items via [`IteratorExt`].
13pub struct Accumulate<'a, I, E> {
14    iter: I,
15    acc: &'a mut Accumulator<E>,
16}
17
18impl<'a, I, E> Accumulate<'a, I, E> {
19    fn new(iter: I, acc: &'a mut Accumulator<E>) -> Self {
20        Accumulate { iter, acc }
21    }
22}
23
24impl<'a, I, E> Iterator for Accumulate<'a, I, E>
25where
26    I: Iterator,
27    I::Item: IntoResultParts,
28    <I::Item as IntoResultParts>::Err: Into<E>,
29{
30    type Item = <I::Item as IntoResultParts>::Ok;
31
32    #[inline]
33    fn next(&mut self) -> Option<Self::Item> {
34        self.iter.find_map(|res| self.acc.handle(res))
35    }
36
37    #[inline]
38    fn size_hint(&self) -> (usize, Option<usize>) {
39        self.iter.size_hint()
40    }
41}
42
43impl<'a, I, E> ExactSizeIterator for Accumulate<'a, I, E>
44where
45    Self: Iterator,
46    I: ExactSizeIterator,
47{
48    #[inline]
49    fn len(&self) -> usize {
50        self.iter.len()
51    }
52}
53
54impl<'a, I, E> FusedIterator for Accumulate<'a, I, E>
55where
56    Self: Iterator,
57    I: FusedIterator,
58{
59}
60
61/// Extends [`Iterator`] with methods for complex error handling:
62pub trait IteratorExt: Iterator {
63    /// Creates an iterator that filters results, collecting errors into an
64    /// [error accumulator](Accumulator) and yielding an iterator over all of
65    /// the "ok" values.
66    ///
67    /// `accumulate` can be used to make chains of [`filter`], [`map`], and
68    /// [`handle`] more concise. The example below shows how a common
69    /// [`filter_map`] call can be shortened.
70    ///
71    /// [`filter`]: Iterator::filter
72    /// [`map`]: Iterator::map
73    /// [`handle`]: Accumulator::handle
74    /// [`filter_map`]: Iterator::filter_map
75    ///
76    /// # Examples
77    ///
78    /// Basic usage:
79    ///
80    /// ```
81    /// use fused_error::{Accumulator, IteratorExt};
82    ///
83    /// let results = [
84    ///     Ok(1),
85    ///     Err("foo"),
86    ///     Ok(3),
87    ///     Err("bar"),
88    ///     Ok(5),
89    /// ];
90    ///
91    /// let mut acc = Accumulator::<&str>::new();
92    ///
93    /// let sum: i32 = results
94    ///     .into_iter()
95    ///     .accumulate(&mut acc)
96    ///     .sum();
97    ///
98    /// assert_eq!(sum, 9);
99    /// assert_eq!(acc.into_vec(), ["foo", "bar"]);
100    /// ```
101    ///
102    /// Here's the same example, but with [`filter_map`]:
103    ///
104    /// ```
105    /// use fused_error::Accumulator;
106    ///
107    /// let results = [
108    ///     Ok(1),
109    ///     Err("foo"),
110    ///     Ok(3),
111    ///     Err("bar"),
112    ///     Ok(5),
113    /// ];
114    ///
115    /// let mut acc = Accumulator::<&str>::new();
116    ///
117    /// let sum: i32 = results
118    ///     .into_iter()
119    ///     .filter_map(|res| acc.handle(res))
120    ///     .sum();
121    ///
122    /// assert_eq!(sum, 9);
123    /// assert_eq!(acc.into_vec(), ["foo", "bar"]);
124    /// ```
125    #[inline]
126    fn accumulate<E>(self, acc: &mut Accumulator<E>) -> Accumulate<Self, E>
127    where
128        Self: Sized,
129        Self::Item: IntoResultParts,
130        <Self::Item as IntoResultParts>::Err: Into<E>,
131    {
132        Accumulate::new(self, acc)
133    }
134
135    /// Drains the errors from an iterator of results into an
136    /// [error accumulator](Accumulator), discarding any "ok" values.
137    ///
138    /// # Examples
139    ///
140    /// ```
141    /// use fused_error::{Accumulator, IteratorExt};
142    ///
143    /// let results = [Err("foo"), Ok(()), Err("bar")];
144    /// let mut accumulator = Accumulator::<&str>::new();
145    ///
146    /// results.into_iter().collect_errors(&mut accumulator);
147    ///
148    /// assert_eq!(accumulator.into_vec(), ["foo", "bar"]);
149    /// ```
150    #[inline]
151    fn collect_errors<E>(self, acc: &mut Accumulator<E>)
152    where
153        Self: Sized,
154        Self::Item: IntoResultParts,
155        <Self::Item as IntoResultParts>::Err: Into<E>,
156    {
157        self.for_each(|res| {
158            let (_, err) = res.into_result_parts();
159            acc.extend(err);
160        });
161    }
162
163    /// Unwraps an iterator of results, collecting all errors into a new
164    /// [error accumulator](Accumulator) and "ok" values into some collection
165    /// that implements [`FromIterator`].
166    ///
167    /// # Examples
168    ///
169    /// ```
170    /// use fused_error::{Accumulator, IteratorExt};
171    ///
172    /// let results = [
173    ///     Ok("foo"),
174    ///     Err("bar"),
175    ///     Ok("baz"),
176    ///     Err("qux"),
177    /// ];
178    ///
179    /// let (vec, acc): (Vec<_>, _) = results.into_iter().partition_results();
180    /// assert_eq!(vec, ["foo", "baz"]);
181    /// assert_eq!(acc.into_vec(), ["bar", "qux"]);
182    /// ```
183    fn partition_results<C>(self) -> (C, Accumulator<<Self::Item as IntoResultParts>::Err>)
184    where
185        Self: Sized,
186        Self::Item: IntoResultParts,
187        C: FromIterator<<Self::Item as IntoResultParts>::Ok>,
188    {
189        let mut acc = Accumulator::new();
190        let c: C = self.accumulate(&mut acc).collect::<C>();
191        (c, acc)
192    }
193}
194
195impl<I> IteratorExt for I where I: Iterator {}