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}