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}