resiter/
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 Oks and Errors.
8pub trait Map<O, E>: Sized {
9    /// Map all `Ok` items while leaving `Err` as is
10    ///
11    /// ```
12    /// use resiter::map::Map;
13    /// use std::str::FromStr;
14    ///
15    /// let mapped: Vec<_> = ["1", "2", "a", "4", "5"]
16    ///     .iter()
17    ///     .map(|txt| usize::from_str(txt))
18    ///     .map_ok(|i| 2 * i)
19    ///     .collect();
20    ///
21    /// assert_eq!(mapped[0], Ok(2));
22    /// assert_eq!(mapped[1], Ok(4));
23    /// assert!(mapped[2].is_err());
24    /// assert_eq!(mapped[3], Ok(8));
25    /// assert_eq!(mapped[4], Ok(10));
26    /// ```
27    fn map_ok<F, O2>(self, _: F) -> MapOk<Self, F>
28    where
29        F: FnMut(O) -> O2;
30
31    /// Map all `Err` items while leaving `Ok` as is
32    ///
33    /// ```
34    /// use resiter::map::Map;
35    /// use std::str::FromStr;
36    /// let mapped: Vec<_> = ["1", "2", "a", "4", "5"]
37    ///     .iter()
38    ///     .map(|txt| usize::from_str(txt))
39    ///     .map_err(|e| format!("{:?}", e))
40    ///     .collect();
41    ///
42    /// assert_eq!(
43    ///     mapped,
44    ///     vec![
45    ///         Ok(1),
46    ///         Ok(2),
47    ///         Err("ParseIntError { kind: InvalidDigit }".to_string()),
48    ///         Ok(4),
49    ///         Ok(5),
50    ///     ]
51    /// );
52    /// ```
53    fn map_err<F, E2>(self, _: F) -> MapErr<Self, F>
54    where
55        F: FnMut(E) -> E2;
56}
57
58impl<I, O, E> Map<O, E> for I
59where
60    I: Iterator<Item = Result<O, E>> + Sized,
61{
62    #[inline]
63    fn map_ok<F, O2>(self, f: F) -> MapOk<Self, F>
64    where
65        F: FnMut(O) -> O2,
66    {
67        MapOk { iter: self, f }
68    }
69
70    #[inline]
71    fn map_err<F, E2>(self, f: F) -> MapErr<Self, F>
72    where
73        F: FnMut(E) -> E2,
74    {
75        MapErr { iter: self, f }
76    }
77}
78
79#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
80pub struct MapOk<I, F> {
81    iter: I,
82    f: F,
83}
84
85impl<I, O, E, F, O2> Iterator for MapOk<I, F>
86where
87    I: Iterator<Item = Result<O, E>>,
88    F: FnMut(O) -> O2,
89{
90    type Item = Result<O2, E>;
91
92    fn next(&mut self) -> Option<Self::Item> {
93        self.iter.next().map(|r| r.map(&mut self.f))
94    }
95
96    #[inline]
97    fn size_hint(&self) -> (usize, Option<usize>) {
98        self.iter.size_hint()
99    }
100}
101
102#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
103pub struct MapErr<I, F> {
104    iter: I,
105    f: F,
106}
107
108impl<I, O, E, F, E2> Iterator for MapErr<I, F>
109where
110    I: Iterator<Item = Result<O, E>>,
111    F: FnMut(E) -> E2,
112{
113    type Item = Result<O, E2>;
114
115    fn next(&mut self) -> Option<Self::Item> {
116        self.iter.next().map(|r| r.map_err(&mut self.f))
117    }
118
119    #[inline]
120    fn size_hint(&self) -> (usize, Option<usize>) {
121        self.iter.size_hint()
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128
129    #[test]
130    fn test_map_ok_hint() {
131        use std::str::FromStr;
132
133        let hint = ["1", "2", "a", "4", "5"]
134            .iter()
135            .map(|txt| usize::from_str(txt))
136            .map_ok(|i| 2 * i)
137            .size_hint();
138
139        assert_eq!(hint, (5, Some(5)));
140    }
141
142    #[test]
143    fn test_map_err_hint() {
144        use std::str::FromStr;
145
146        let hint = ["1", "2", "a", "4", "5"]
147            .iter()
148            .map(|txt| usize::from_str(txt))
149            .map_err(|e| format!("{:?}", e))
150            .size_hint();
151
152        assert_eq!(hint, (5, Some(5)));
153    }
154}