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 {}