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}