resiter/
try_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 for `Iterator<Item = Result<O, E>>` to filter the Ok(_) and leaving the Err(_) as
8/// is, but allowing the filter to return a `Result<bool, E>` itself
9pub trait TryFilter<O, E>: Sized {
10    /// Filters every `Ok`-value with a function that can return an Err.
11    /// Useful when the filter condition uses functions that can fail.
12    ///
13    ///```
14    /// use resiter::try_filter::TryFilter;
15    /// use std::str::FromStr;
16    ///
17    /// let v = ["1", "2", "4", "a", "5"]
18    ///     .iter()
19    ///     .map(Ok)
20    ///     .try_filter_ok(|e| usize::from_str(e).map(|n| n < 3))
21    ///     .collect::<Vec<Result<_, _>>>();
22    ///
23    /// assert_eq!(v.len(), 3);
24    /// assert_eq!(v.iter().filter(|x| x.is_ok()).count(), 2);
25    /// assert_eq!(v.iter().filter(|x| x.is_err()).count(), 1);
26    ///```
27    fn try_filter_ok<F>(self, _: F) -> TryFilterOk<Self, F>
28    where
29        F: FnMut(&O) -> Result<bool, E>;
30
31    /// Filters every `Err`-value with a function that can return an Err.
32    /// Useful when the filter condition uses functions that can fail.
33    ///
34    /// ```
35    /// use resiter::try_filter::TryFilter;
36    /// use std::num::ParseIntError;
37    /// use std::str::FromStr;
38    ///
39    /// let v = ["1", "2", "4", "a", "5"]
40    ///     .iter()
41    ///     .map(|txt| usize::from_str(txt))
42    ///     .try_filter_err(|_:&ParseIntError| Ok(false))
43    ///     .collect::<Vec<Result<_, _>>>();
44    ///
45    /// assert_eq!(v.iter().filter(|x| x.is_ok()).count(), 4);
46    /// assert_eq!(v.iter().filter(|x| x.is_err()).count(), 0);
47    /// ```
48    fn try_filter_err<F>(self, _: F) -> TryFilterErr<Self, F>
49    where
50        F: FnMut(&E) -> Result<bool, E>;
51}
52
53impl<I, O, E> TryFilter<O, E> for I
54where
55    I: Iterator<Item = Result<O, E>> + Sized,
56{
57    #[inline]
58    fn try_filter_ok<F>(self, f: F) -> TryFilterOk<Self, F>
59    where
60        F: FnMut(&O) -> Result<bool, E>,
61    {
62        TryFilterOk { iter: self, f }
63    }
64
65    #[inline]
66    fn try_filter_err<F>(self, f: F) -> TryFilterErr<Self, F>
67    where
68        F: FnMut(&E) -> Result<bool, E>,
69    {
70        TryFilterErr { iter: self, f }
71    }
72}
73
74#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
75pub struct TryFilterOk<I, F> {
76    iter: I,
77    f: F,
78}
79
80#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
81pub struct TryFilterErr<I, F> {
82    iter: I,
83    f: F,
84}
85
86impl<I, O, E, F> Iterator for TryFilterOk<I, F>
87where
88    I: Iterator<Item = Result<O, E>>,
89    F: FnMut(&O) -> Result<bool, E>,
90{
91    type Item = Result<O, E>;
92
93    fn next(&mut self) -> Option<Self::Item> {
94        loop {
95            return match self.iter.next() {
96                Some(Ok(x)) => match (self.f)(&x) {
97                    Ok(true) => Some(Ok(x)),
98                    Ok(false) => continue,
99                    Err(e) => Some(Err(e)),
100                },
101                other => other,
102            };
103        }
104    }
105
106    #[inline]
107    fn size_hint(&self) -> (usize, Option<usize>) {
108        let hint_sup = self.iter.size_hint().1;
109        (0, hint_sup)
110    }
111}
112
113impl<I, O, E, F> Iterator for TryFilterErr<I, F>
114where
115    I: Iterator<Item = Result<O, E>>,
116    F: FnMut(&E) -> Result<bool, E>,
117{
118    type Item = Result<O, E>;
119
120    fn next(&mut self) -> Option<Self::Item> {
121        loop {
122            return match self.iter.next() {
123                Some(Err(x)) => match (self.f)(&x) {
124                    Ok(true) => Some(Err(x)),
125                    Ok(false) => continue,
126                    Err(e) => Some(Err(e)),
127                },
128                other => other,
129            };
130        }
131    }
132
133    #[inline]
134    fn size_hint(&self) -> (usize, Option<usize>) {
135        let hint_sup = self.iter.size_hint().1;
136        (0, hint_sup)
137    }
138}