resiter/try_filter_map.rs
1//
2// This Source Code Form is subject to the terms of the Mozilla Public
3// License, v. 2.0. If a copy of the MPL was not distributed with this
4// file, You can obtain one at http://mozilla.org/MPL/2.0/.
5//
6
7/// Extension trait for `Iterator<Item = Result<O, E>>` to selectively transform and map Oks and Errors.
8pub trait TryFilterMap<O, E>: Sized {
9 /// Equivalent to [Iterator::filter_map] on all `Ok` values.
10 /// The filter function can fail with a result and turn an
11 /// [Result::Ok] into a [Result::Err]
12 ///
13 /// ```
14 /// use std::str::FromStr;
15 /// use resiter::try_filter_map::TryFilterMap;
16 ///
17 /// let filter_mapped: Vec<_> = vec![
18 /// Ok("1"),
19 /// Err("2".to_owned()),
20 /// Ok("a"), // will become an error
21 /// Err("4".to_owned()),
22 /// Ok("5"), // will be filtered out
23 /// Err("b".to_owned()),
24 /// Err("8".to_owned()),
25 /// ]
26 /// .into_iter()
27 /// .try_filter_map_ok(|txt| {
28 /// match usize::from_str(txt).map_err(|e| e.to_string()) {
29 /// Err(e) => Some(Err(e)),
30 /// Ok(u) => {
31 /// if u < 3 {
32 /// Some(Ok(u))
33 /// } else {
34 /// None
35 /// }
36 /// }
37 /// }
38 /// })
39 /// .collect();
40 ///
41 /// assert_eq!(
42 /// filter_mapped,
43 /// [
44 /// Ok(1),
45 /// Err("2".to_owned()),
46 /// Err("invalid digit found in string".to_owned()),
47 /// Err("4".to_owned()),
48 /// Err("b".to_owned()),
49 /// Err("8".to_owned())
50 /// ]
51 /// );
52 /// ```
53 fn try_filter_map_ok<F, O2>(self, _: F) -> TryFilterMapOk<Self, F>
54 where
55 F: FnMut(O) -> Option<Result<O2, E>>;
56
57 /// Equivalent to [Iterator::filter_map] on all `Err` values.
58 /// The filter function can fail with a result and turn a
59 /// [Result::Err] into a [Result::Ok]
60 ///
61 /// ```
62 /// use std::str::FromStr;
63 /// use resiter::try_filter_map::TryFilterMap;
64 ///
65 /// let filter_mapped: Vec<_> = vec![
66 /// Ok("1".to_owned()),
67 /// Err("2".to_owned()), // will become ok
68 /// Ok("a".to_owned()),
69 /// Err("4".to_owned()), // will be removed
70 /// Ok("5".to_owned()),
71 /// Err("b".to_owned()), // will be an error
72 /// Err("8".to_owned()), // will be removed
73 /// ]
74 /// .into_iter()
75 /// .try_filter_map_err(|txt| {
76 /// match usize::from_str(&txt).map_err(|e| e.to_string()) {
77 /// Err(e) => Some(Err(e)),
78 /// Ok(u) => {
79 /// if u < 3 {
80 /// Some(Ok(u.to_string()))
81 /// } else {
82 /// None
83 /// }
84 /// }
85 /// }
86 /// })
87 /// .collect();
88 ///
89 /// assert_eq!(
90 /// filter_mapped,
91 /// [
92 /// Ok("1".to_owned()),
93 /// Ok("2".to_owned()),
94 /// Ok("a".to_owned()),
95 /// Ok("5".to_owned()),
96 /// Err("invalid digit found in string".to_owned()),
97 /// ]
98 /// );
99 /// ```
100 fn try_filter_map_err<F, E2>(self, _: F) -> TryFilterMapErr<Self, F>
101 where
102 F: FnMut(E) -> Option<Result<O, E2>>;
103}
104
105impl<I, O, E> TryFilterMap<O, E> for I
106where
107 I: Iterator<Item = Result<O, E>> + Sized,
108{
109 #[inline]
110 fn try_filter_map_ok<F, O2>(self, f: F) -> TryFilterMapOk<Self, F>
111 where
112 F: FnMut(O) -> Option<Result<O2, E>>,
113 {
114 TryFilterMapOk { iter: self, f }
115 }
116
117 #[inline]
118 fn try_filter_map_err<F, E2>(self, f: F) -> TryFilterMapErr<Self, F>
119 where
120 F: FnMut(E) -> Option<Result<O, E2>>,
121 {
122 TryFilterMapErr { iter: self, f }
123 }
124}
125
126#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
127pub struct TryFilterMapOk<I, F> {
128 iter: I,
129 f: F,
130}
131
132impl<I, O, E, F, O2> Iterator for TryFilterMapOk<I, F>
133where
134 I: Iterator<Item = Result<O, E>>,
135 F: FnMut(O) -> Option<Result<O2, E>>,
136{
137 type Item = Result<O2, E>;
138
139 fn next(&mut self) -> Option<Self::Item> {
140 loop {
141 return match self.iter.next() {
142 Some(Ok(x)) => match (self.f)(x) {
143 Some(r) => Some(r),
144 None => continue,
145 },
146 Some(Err(e)) => Some(Err(e)),
147 None => None,
148 };
149 }
150 }
151
152 #[inline]
153 fn size_hint(&self) -> (usize, Option<usize>) {
154 self.iter.size_hint()
155 }
156}
157
158#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
159pub struct TryFilterMapErr<I, F> {
160 iter: I,
161 f: F,
162}
163
164impl<I, O, E, E2, F> Iterator for TryFilterMapErr<I, F>
165where
166 I: Iterator<Item = Result<O, E>>,
167 F: FnMut(E) -> Option<Result<O, E2>>,
168{
169 type Item = Result<O, E2>;
170
171 fn next(&mut self) -> Option<Self::Item> {
172 loop {
173 return match self.iter.next() {
174 Some(Err(x)) => match (self.f)(x) {
175 Some(r) => Some(r),
176 None => continue,
177 },
178 Some(Ok(x)) => Some(Ok(x)),
179 None => None,
180 };
181 }
182 }
183
184 #[inline]
185 fn size_hint(&self) -> (usize, Option<usize>) {
186 self.iter.size_hint()
187 }
188}