iter_fi/
lib.rs

1//! Boolean composable iterators.
2//!
3//! This crate adds the [`iter_if`] method to all [`Iterator`]s.
4//! If its `condition` is `true`, it calls a function and returns its result.
5//! Otherwise, it returns `self`.
6//!
7//! ## Is this useful?
8//!
9//! I have no idea. I made this because I had kind-of-a-use-case for `IterIf`.
10//!
11//! ## Crate features
12//!
13//! Both features are enabled by default.
14//!
15//! - `branched`: allows iteration over iterators with two different types and adds a dependency
16//!   on [`either`]
17//! - `std`: disabling this will make the crate `#[no_std]`
18//!
19//! [`iter_if`]: crate::IterFi::iter_if
20#![warn(clippy::pedantic)]
21#![warn(clippy::perf)]
22#![warn(missing_docs)]
23#![cfg_attr(not(feature = "std"), no_std)]
24
25mod branching;
26
27#[cfg(feature = "branched")]
28pub use branching::Branched;
29
30use core::iter::Map;
31
32/// Boolean composable iterators.
33///
34/// This trait adds the [`iter_if`] method which takes a boolean condition and a function.
35/// If the condition is met, the function is run and the returned iterator is used as output.
36/// Otherwise, the call is ignored and the original iterator continues.
37///
38/// This trait has an automatic blanket implementation for all [`Iterator`]s.
39/// You do not need to implement it on your own iterators.
40///
41/// [`iter_if`]: crate::IterFi::iter_if
42pub trait IterFi: Iterator {
43    /// If `condition` is `true`, return the iterator returned by `func`.
44    /// Otherwise, skip this call and return `self`.
45    ///
46    /// Note that if [`T::Item`] is not the same as `F::Item`, you will need to call [`branched`]
47    /// after this to get an iterator.
48    ///
49    /// # Examples
50    ///
51    /// When the condition is true, the function is applied:
52    ///
53    /// ```
54    /// use iter_fi::IterFi;
55    ///
56    /// let numbers = (0..5).iter_if(true, Iterator::rev).collect::<Vec<_>>();
57    /// assert_eq!(numbers, vec![4, 3, 2, 1, 0]);
58    /// ```
59    ///
60    /// When the condition is false, the function is ignored:
61    ///
62    /// ```
63    /// use iter_fi::IterFi;
64    ///
65    /// let numbers = (0..5).iter_if(false, Iterator::rev).collect::<Vec<_>>();
66    /// assert_eq!(numbers, vec![0, 1, 2, 3, 4]);
67    /// ```
68    ///
69    /// [`T::Item`]: Iterator::Item
70    /// [`branched`]: IterIf::branched
71    fn iter_if<C, T: Iterator>(self, condition: bool, func: C) -> IterIf<T, Self>
72    where
73        Self: Sized,
74        C: Fn(Self) -> T,
75    {
76        if condition {
77            IterIf::True(func(self))
78        } else {
79            IterIf::False(self)
80        }
81    }
82
83    /// If `condition` is `true`, apply `func` to elements in `self`.
84    /// Otherwise, skip this call and return `self`.
85    ///
86    /// This is a shorthand for `iter_if(condition, |i| i.map(func))`.
87    /// See the documentation for [`iter_if`] for more.
88    ///
89    /// # Examples
90    ///
91    /// When the condition is true, the map is applied:
92    ///
93    /// ```
94    /// use iter_fi::IterFi;
95    ///
96    /// let numbers = (0..5).map_if(true, |n| n * 2).collect::<Vec<_>>();
97    /// assert_eq!(numbers, vec![0, 2, 4, 6, 8]);
98    /// ```
99    ///
100    /// When the condition is false, the map is ignored:
101    ///
102    /// ```
103    /// use iter_fi::IterFi;
104    ///
105    /// let numbers = (0..5).map_if(false, |n| n * 2).collect::<Vec<_>>();
106    /// assert_eq!(numbers, vec![0, 1, 2, 3, 4]);
107    /// ```
108    ///
109    /// [`iter_if`]: IterFi::iter_if
110    fn map_if<C, T>(self, condition: bool, func: C) -> IterIf<Map<Self, C>, Self>
111    where
112        Self: Sized,
113        C: Fn(Self::Item) -> T,
114    {
115        if condition {
116            IterIf::True(self.map(func))
117        } else {
118            IterIf::False(self)
119        }
120    }
121}
122
123impl<I: Iterator> IterFi for I {}
124
125/// An iterator which yields items from `T` if the original condition was true
126/// and `F` if not.
127///
128/// This struct is created by the [`iter_if`] method on [`IterFi`]. See its documentation for more.
129///
130/// [`iter_if`]: crate::IterFi::iter_if
131/// [`IterFi`]: crate::IterFi
132#[derive(Debug, Clone, Copy)]
133pub enum IterIf<T: Iterator, F: Iterator> {
134    /// Variant if the original condition was true.
135    True(T),
136    /// Variant if the original condition was false.
137    False(F),
138}
139
140impl<T: Iterator<Item = I>, F: Iterator<Item = I>, I> Iterator for IterIf<T, F> {
141    type Item = I;
142
143    fn next(&mut self) -> Option<Self::Item> {
144        match self {
145            IterIf::True(t) => t.next(),
146            IterIf::False(f) => f.next(),
147        }
148    }
149}
150
151impl<T: Iterator, F: Iterator> IterIf<T, F> {
152    /// Convert this boolean iterator to a branched iterator.
153    ///
154    /// The `Branched` iterator can iterate over two different types.
155    /// It is only useful if the two iterators have different [`Iterator::Item`]s.
156    /// If they are the same, you can iterate directly over the [`IterIf`].
157    ///
158    /// # Examples
159    ///
160    /// ```
161    /// use iter_fi::IterFi;
162    /// use either::Either;
163    ///
164    /// let mut something = ('a'..'f')
165    ///     .map_if(true, |c| c as u8)
166    ///     .branched();
167    /// assert_eq!(something.next(), Some(Either::Left(97)));
168    /// assert_eq!(something.next(), Some(Either::Left(98)));
169    ///
170    /// let mut something_else = ('a'..'f')
171    ///     .map_if(false, |c| c as u8)
172    ///     .branched();
173    /// assert_eq!(something_else.next(), Some(Either::Right('a')));
174    /// assert_eq!(something_else.next(), Some(Either::Right('b')));
175    /// ```
176    #[cfg(feature = "branched")]
177    pub fn branched(self) -> Branched<T, F> {
178        Branched(self)
179    }
180
181    /// If the original condition was false, apply `func` to the iterator. Otherwise, pass.
182    ///
183    /// # Examples
184    ///
185    /// ```
186    /// use iter_fi::IterFi;
187    ///
188    /// let some_numbers = (0..5)
189    ///     .iter_if(true, Iterator::rev)
190    ///     .else_iter(|i| i.skip(3))
191    ///     .collect::<Vec<_>>();
192    /// assert_eq!(some_numbers, vec![4, 3, 2, 1, 0]);
193    ///
194    /// let some_more_numbers = (0..5)
195    ///     .iter_if(false, Iterator::rev)
196    ///     .else_iter(|i| i.skip(3))
197    ///     .collect::<Vec<_>>();
198    /// assert_eq!(some_more_numbers, vec![3, 4]);
199    /// ```
200    pub fn else_iter<C, N: Iterator>(self, func: C) -> IterIf<T, N>
201    where
202        C: Fn(F) -> N,
203    {
204        match self {
205            Self::True(t) => IterIf::True(t),
206            Self::False(f) => IterIf::False(func(f)),
207        }
208    }
209
210    /// If the original condition was false, map `func` over the iterator. Otherwise, pass.
211    ///
212    /// # Examples
213    ///
214    /// ```
215    /// use iter_fi::IterFi;
216    ///
217    /// let some_numbers = (0..5)
218    ///     .map_if(true, |n| n * 2)
219    ///     .else_map(|n| n + 3)
220    ///     .collect::<Vec<_>>();
221    /// assert_eq!(some_numbers, vec![0, 2, 4, 6, 8]);
222    ///
223    /// let some_more_numbers = (0..5)
224    ///     .map_if(false, |n| n * 2)
225    ///     .else_map(|n| n + 3)
226    ///     .collect::<Vec<_>>();
227    /// assert_eq!(some_more_numbers, vec![3, 4, 5, 6, 7]);
228    /// ```
229    ///
230    /// [`else_iter`]: IterIf::else_iter
231    pub fn else_map<C, N>(self, func: C) -> IterIf<T, Map<F, C>>
232    where
233        C: Fn(F::Item) -> N,
234    {
235        match self {
236            Self::True(t) => IterIf::True(t),
237            Self::False(f) => IterIf::False(f.map(func)),
238        }
239    }
240
241    /// Return `Some(T)` if the condition was true and `None` otherwise.
242    ///
243    /// # Examples
244    ///
245    /// ```
246    /// use iter_fi::IterFi;
247    ///
248    /// let numbers = (0..5)
249    ///     .iter_if(true, Iterator::rev)
250    ///     .as_true()
251    ///     .unwrap()
252    ///     .collect::<Vec<_>>();
253    /// assert_eq!(numbers, vec![4, 3, 2, 1, 0]);
254    ///
255    /// let more_numbers = (0..5)
256    ///     .iter_if(false, Iterator::rev)
257    ///     .as_true();
258    /// assert!(more_numbers.is_none());
259    /// ```
260    pub fn as_true(self) -> Option<T> {
261        match self {
262            IterIf::True(t) => Some(t),
263            IterIf::False(_) => None,
264        }
265    }
266
267    /// Return `Some(F)` if the condition was false and `None` otherwise.
268    ///
269    /// # Examples
270    ///
271    /// ```
272    /// use iter_fi::IterFi;
273    ///
274    /// let numbers = (0..5)
275    ///     .iter_if(false, Iterator::rev)
276    ///     .as_false()
277    ///     .unwrap()
278    ///     .collect::<Vec<_>>();
279    /// assert_eq!(numbers, vec![0, 1, 2, 3, 4]);
280    ///
281    /// let more_numbers = (0..5)
282    ///     .iter_if(true, Iterator::rev)
283    ///     .as_false();
284    /// assert!(more_numbers.is_none());
285    /// ```
286    pub fn as_false(self) -> Option<F> {
287        match self {
288            IterIf::True(_) => None,
289            IterIf::False(f) => Some(f),
290        }
291    }
292}