resiter/
filter.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 filter one kind of result (and leaving the other as is)
8pub trait Filter<O, E>: Sized {
9    /// Filter `Ok` items while keeping `Err`
10    ///
11    /// ```
12    /// use resiter::filter::Filter;
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    ///     .filter_ok(|i| i % 2 == 0)
19    ///     .collect();
20    /// assert_eq!(mapped.len(), 3);
21    /// assert_eq!(mapped[0], Ok(2));
22    /// assert!(mapped[1].is_err());
23    /// assert_eq!(mapped[2], Ok(4))
24    /// ```
25    fn filter_ok<F>(self, _: F) -> FilterOk<Self, F>
26    where
27        F: FnMut(&O) -> bool;
28
29    /// Filter `Err` values while keeping `Ok`
30    ///
31    /// ```
32    /// use resiter::filter::Filter;
33    /// use std::str::FromStr;
34    ///
35    /// let mapped: Vec<_> = ["1", "2", "a", "4", "5"]
36    ///     .iter()
37    ///     .map(|txt| usize::from_str(txt))
38    ///     .filter_err(|_| false)
39    ///     .collect();
40    ///
41    /// assert_eq!(mapped, vec![Ok(1), Ok(2), Ok(4), Ok(5)]);
42    /// ```
43    fn filter_err<F>(self, _: F) -> FilterErr<Self, F>
44    where
45        F: FnMut(&E) -> bool;
46}
47
48impl<I, O, E> Filter<O, E> for I
49where
50    I: Iterator<Item = Result<O, E>> + Sized,
51{
52    #[inline]
53    fn filter_ok<F>(self, f: F) -> FilterOk<Self, F>
54    where
55        F: FnMut(&O) -> bool,
56    {
57        FilterOk { iter: self, f }
58    }
59
60    #[inline]
61    fn filter_err<F>(self, f: F) -> FilterErr<Self, F>
62    where
63        F: FnMut(&E) -> bool,
64    {
65        FilterErr { iter: self, f }
66    }
67}
68
69#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
70pub struct FilterOk<I, F> {
71    iter: I,
72    f: F,
73}
74
75impl<I, O, E, F> Iterator for FilterOk<I, F>
76where
77    I: Iterator<Item = Result<O, E>>,
78    F: FnMut(&O) -> bool,
79{
80    type Item = Result<O, E>;
81
82    fn next(&mut self) -> Option<Self::Item> {
83        loop {
84            match self.iter.next() {
85                Some(Ok(x)) => {
86                    if (self.f)(&x) {
87                        return Some(Ok(x));
88                    }
89                }
90                other => {
91                    return other;
92                }
93            }
94        }
95    }
96
97    #[inline]
98    fn size_hint(&self) -> (usize, Option<usize>) {
99        let hint_sup = self.iter.size_hint().1;
100        (0, hint_sup)
101    }
102}
103
104#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
105pub struct FilterErr<I, F> {
106    iter: I,
107    f: F,
108}
109
110impl<I, O, E, F> Iterator for FilterErr<I, F>
111where
112    I: Iterator<Item = Result<O, E>>,
113    F: FnMut(&E) -> bool,
114{
115    type Item = Result<O, E>;
116
117    fn next(&mut self) -> Option<Self::Item> {
118        loop {
119            match self.iter.next() {
120                Some(Err(x)) => {
121                    if (self.f)(&x) {
122                        return Some(Err(x));
123                    }
124                }
125                other => {
126                    return other;
127                }
128            }
129        }
130    }
131
132    #[inline]
133    fn size_hint(&self) -> (usize, Option<usize>) {
134        let hint_sup = self.iter.size_hint().1;
135        (0, hint_sup)
136    }
137}
138
139#[cfg(test)]
140mod tests {
141    use super::*;
142
143    #[test]
144    fn test_filter_ok_hint() {
145        use std::str::FromStr;
146
147        let hint = ["1", "2", "a", "4", "5"]
148            .iter()
149            .map(|txt| usize::from_str(txt))
150            .filter_ok(|i| i % 2 == 0)
151            .size_hint();
152
153        assert_eq!(hint, (0, Some(5)));
154    }
155
156    #[test]
157    fn test_filter_err_hint() {
158        use std::str::FromStr;
159
160        let hint = ["1", "2", "a", "4", "5"]
161            .iter()
162            .map(|txt| usize::from_str(txt))
163            .filter_err(|_| false)
164            .size_hint();
165
166        assert_eq!(hint, (0, Some(5)));
167    }
168}