resiter/
try_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 TryMap<O, E>: Sized {
9    /// Equivalent to [Iterator::map] on all `Ok` values.
10    /// The map function can fail with a result and turn a
11    /// [Result::Ok] into a [Result::Err]
12    ///
13    /// ```
14    /// use std::str::FromStr;
15    /// use resiter::try_map::TryMap;
16    ///
17    /// let 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 too high
23    ///     Err("b".to_owned()),
24    ///     Err("8".to_owned()),
25    /// ]
26    /// .into_iter()
27    /// .try_map_ok(|txt| {
28    ///     let n = usize::from_str(txt).map_err(|e| e.to_string())?;
29    ///     if n < 3 {
30    ///        Ok(n)
31    ///     }
32    ///     else {
33    ///        Err("Too high".to_string())
34    ///     }
35    /// })
36    /// .collect();
37    ///
38    /// assert_eq!(
39    ///     mapped,
40    ///     [
41    ///         Ok(1),
42    ///         Err("2".to_owned()),
43    ///         Err("invalid digit found in string".to_owned()),
44    ///         Err("4".to_owned()),
45    ///         Err("Too high".to_owned()),
46    ///         Err("b".to_owned()),
47    ///         Err("8".to_owned())
48    ///     ]
49    /// );
50    /// ```
51    fn try_map_ok<F, O2>(self, _: F) -> TryMapOk<Self, F>
52    where
53        F: FnMut(O) -> Result<O2, E>;
54
55    /// Equivalent to [Iterator::map] on all `Err` values.
56    /// The map function can fail with a result and turn a
57    /// [Result::Err] into a [Result::Ok] or another [Result::Err]
58    /// possibly changing it's error type
59    ///
60    /// ```
61    /// use std::str::FromStr;
62    /// use resiter::try_map::TryMap;
63    ///
64    /// let mapped: Vec<_> = vec![
65    ///     Ok(1),
66    ///     Err("2".to_owned()), // will become ok
67    ///     Ok(3),
68    ///     Err("4".to_owned()), // will be "Too high"
69    ///     Ok(5),
70    ///     Err("b".to_owned()), // will be an error
71    ///     Err("8".to_owned()), // will be "Too high"
72    /// ]
73    /// .into_iter()
74    /// .try_map_err(|txt| {
75    ///     let n = usize::from_str(&txt).map_err(|e| e.to_string())?;
76    ///     if n < 4 {
77    ///         Ok(n)
78    ///     }
79    ///     else {
80    ///         Err("Too high".to_owned())
81    ///     }
82    /// })
83    /// .collect();
84    ///
85    /// assert_eq!(
86    ///     mapped,
87    ///     [
88    ///         Ok(1),
89    ///         Ok(2),
90    ///         Ok(3),
91    ///         Err("Too high".to_owned()),
92    ///         Ok(5),
93    ///         Err("invalid digit found in string".to_owned()),
94    ///         Err("Too high".to_owned()),
95    ///     ]
96    /// );
97    /// ```
98    fn try_map_err<F, E2>(self, _: F) -> TryMapErr<Self, F>
99    where
100        F: FnMut(E) -> Result<O, E2>;
101}
102
103impl<I, O, E> TryMap<O, E> for I
104where
105    I: Iterator<Item = Result<O, E>> + Sized,
106{
107    #[inline]
108    fn try_map_ok<F, O2>(self, f: F) -> TryMapOk<Self, F>
109    where
110        F: FnMut(O) -> Result<O2, E>,
111    {
112        TryMapOk { iter: self, f }
113    }
114
115    #[inline]
116    fn try_map_err<F, E2>(self, f: F) -> TryMapErr<Self, F>
117    where
118        F: FnMut(E) -> Result<O, E2>,
119    {
120        TryMapErr { iter: self, f }
121    }
122}
123
124pub struct TryMapOk<I, F> {
125    iter: I,
126    f: F,
127}
128
129impl<I, O, E, F, O2> Iterator for TryMapOk<I, F>
130where
131    I: Iterator<Item = Result<O, E>>,
132    F: FnMut(O) -> Result<O2, E>,
133{
134    type Item = Result<O2, E>;
135
136    fn next(&mut self) -> Option<Self::Item> {
137        match self.iter.next() {
138            Some(Ok(x)) => Some((self.f)(x)),
139            Some(Err(e)) => Some(Err(e)),
140            None => None,
141        }
142    }
143
144    #[inline]
145    fn size_hint(&self) -> (usize, Option<usize>) {
146        self.iter.size_hint()
147    }
148}
149
150#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
151pub struct TryMapErr<I, F> {
152    iter: I,
153    f: F,
154}
155
156impl<I, O, E, E2, F> Iterator for TryMapErr<I, F>
157where
158    I: Iterator<Item = Result<O, E>>,
159    F: FnMut(E) -> Result<O, E2>,
160{
161    type Item = Result<O, E2>;
162
163    fn next(&mut self) -> Option<Self::Item> {
164        match self.iter.next() {
165            Some(Err(x)) => Some((self.f)(x)),
166            Some(Ok(x)) => Some(Ok(x)),
167            None => None,
168        }
169    }
170
171    #[inline]
172    fn size_hint(&self) -> (usize, Option<usize>) {
173        self.iter.size_hint()
174    }
175}