stry_common/utils/fenn/
iter.rs

1//! Extension traits for [`Iterator`] and [`IntoIterator`].
2//!
3//! [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
4//! [`IntoIterator`]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html
5
6/// Extensions to [`IntoIterator`] that allow for conditionally chaining other
7/// [`IntoIterator`]'s.
8///
9/// [`IntoIterator`]: https://doc.rust-lang.org/std/iter/trait.IntoIterator.html
10pub trait IntoIteratorExt: IntoIterator + Sized {
11    /// Conditionally chains a given iterator.
12    ///
13    /// # Example
14    ///
15    /// ```
16    /// use fenn::iter::IntoIteratorExt;
17    ///
18    /// let vec1 = vec![1, 2, 3];
19    /// let vec2 = vec![4, 5, 6];
20    ///
21    /// let new_vec = vec1.chain_if(true, vec2).collect::<Vec<_>>();
22    ///
23    /// assert_eq!(vec![1, 2, 3, 4, 5, 6], new_vec);
24    /// ```
25    ///
26    /// ```
27    /// use fenn::iter::IntoIteratorExt;
28    ///
29    /// let vec1 = vec![1, 2, 3];
30    /// let vec2 = vec![4, 5, 6];
31    ///
32    /// let new_vec = vec1.chain_if(false, vec2).collect::<Vec<_>>();
33    ///
34    /// assert_eq!(vec![1, 2, 3], new_vec);
35    /// ```
36    fn chain_if<C>(self, cond: bool, iter: C) -> ChainIf<Self, C>
37    where
38        C: IntoIterator<Item = Self::Item>,
39    {
40        self.chain_if_with(cond, || iter)
41    }
42
43    /// Conditionally chains a given iterator falling back on another.
44    ///
45    /// # Example
46    ///
47    /// ```
48    /// use fenn::iter::IntoIteratorExt;
49    ///
50    /// let vec1 = vec![1, 2, 3];
51    /// let vec2 = vec![4, 5, 6];
52    /// let vec3 = vec![7, 8, 9];
53    ///
54    /// let new_vec = vec1.chain_if_else(true, vec2, vec3).collect::<Vec<_>>();
55    ///
56    /// assert_eq!(vec![1, 2, 3, 4, 5, 6], new_vec);
57    /// ```
58    ///
59    /// ```
60    /// use fenn::iter::IntoIteratorExt;
61    ///
62    /// let vec1 = vec![1, 2, 3];
63    /// let vec2 = vec![4, 5, 6];
64    /// let vec3 = vec![7, 8, 9];
65    ///
66    /// let new_vec = vec1.chain_if_else(false, vec2, vec3).collect::<Vec<_>>();
67    ///
68    /// assert_eq!(vec![1, 2, 3, 7, 8, 9], new_vec);
69    /// ```
70    fn chain_if_else<I, E>(self, cond: bool, iter_if: I, iter_else: E) -> ChainIfElse<Self, I, E>
71    where
72        I: IntoIterator<Item = Self::Item>,
73        E: IntoIterator<Item = Self::Item>,
74    {
75        self.chain_if_else_with(cond, || iter_if, || iter_else)
76    }
77
78    /// Conditionally chains a given iterator.
79    ///
80    /// Like `IteratorExt::chain_if`, but instead accept functions
81    /// that return the iterator.
82    ///
83    /// # Example
84    ///
85    /// ```
86    /// use fenn::iter::IntoIteratorExt;
87    ///
88    /// let vec1 = vec![1, 2, 3];
89    /// let vec2 = vec![4, 5, 6];
90    ///
91    /// let new_vec = vec1.chain_if_with(true, || vec2).collect::<Vec<_>>();
92    ///
93    /// assert_eq!(vec![1, 2, 3, 4, 5, 6], new_vec);
94    /// ```
95    ///
96    /// ```
97    /// use fenn::iter::IntoIteratorExt;
98    ///
99    /// let vec1 = vec![1, 2, 3];
100    /// let vec2 = vec![4, 5, 6];
101    ///
102    /// let new_vec = vec1.chain_if_with(false, || vec2).collect::<Vec<_>>();
103    ///
104    /// assert_eq!(vec![1, 2, 3], new_vec);
105    /// ```
106    fn chain_if_with<F, C>(self, cond: bool, fun: F) -> ChainIf<Self, C>
107    where
108        F: FnOnce() -> C,
109        C: IntoIterator<Item = Self::Item>;
110
111    /// Conditionally chains a given iterator falling back on another.
112    ///
113    /// Like `IteratorExt::chain_if_else`, but instead accept functions
114    /// that return the iterator.
115    ///
116    /// # Example
117    ///
118    /// ```
119    /// use fenn::iter::IntoIteratorExt;
120    ///
121    /// let vec1 = vec![1, 2, 3];
122    /// let vec2 = vec![4, 5, 6];
123    /// let vec3 = vec![7, 8, 9];
124    ///
125    /// let new_vec = vec1.chain_if_else_with(true, || vec2, || vec3).collect::<Vec<_>>();
126    ///
127    /// assert_eq!(vec![1, 2, 3, 4, 5, 6], new_vec);
128    /// ```
129    ///
130    /// ```
131    /// use fenn::iter::IntoIteratorExt;
132    ///
133    /// let vec1 = vec![1, 2, 3];
134    /// let vec2 = vec![4, 5, 6];
135    /// let vec3 = vec![7, 8, 9];
136    ///
137    /// let new_vec = vec1.chain_if_else_with(false, || vec2, || vec3).collect::<Vec<_>>();
138    ///
139    /// assert_eq!(vec![1, 2, 3, 7, 8, 9], new_vec);
140    /// ```
141    fn chain_if_else_with<FI, I, FE, E>(
142        self,
143        cond: bool,
144        fun_if: FI,
145        fun_else: FE,
146    ) -> ChainIfElse<Self, I, E>
147    where
148        FI: FnOnce() -> I,
149        I: IntoIterator<Item = Self::Item>,
150        FE: FnOnce() -> E,
151        E: IntoIterator<Item = Self::Item>;
152}
153
154impl<T> IntoIteratorExt for T
155where
156    T: IntoIterator + Sized,
157{
158    fn chain_if_with<F, C>(self, cond: bool, fun: F) -> ChainIf<Self, C>
159    where
160        F: FnOnce() -> C,
161        C: IntoIterator<Item = Self::Item>,
162    {
163        if cond {
164            ChainIf::Chained(self.into_iter(), fun().into_iter())
165        } else {
166            ChainIf::Original(self.into_iter())
167        }
168    }
169
170    fn chain_if_else_with<FI, I, FE, E>(
171        self,
172        cond: bool,
173        fun_if: FI,
174        fun_else: FE,
175    ) -> ChainIfElse<Self, I, E>
176    where
177        FI: FnOnce() -> I,
178        I: IntoIterator<Item = Self::Item>,
179        FE: FnOnce() -> E,
180        E: IntoIterator<Item = Self::Item>,
181    {
182        if cond {
183            ChainIfElse::If(self.into_iter(), fun_if().into_iter())
184        } else {
185            ChainIfElse::Else(self.into_iter(), fun_else().into_iter())
186        }
187    }
188}
189
190/// An iterator that conditionally chains together other iterators.
191///
192/// This `enum` is created by the [`IteratorExt::chain_if`] and
193/// [`IteratorExt::chain_if_with`] functions.
194///
195/// [`IteratorExt::chain_if`]: ./trait.IteratorExt.html#method.chain_if
196/// [`IteratorExt::chain_if_with`]: ./trait.IteratorExt.html#method.chain_if_with
197pub enum ChainIf<O, C>
198where
199    O: IntoIterator,
200    C: IntoIterator<Item = O::Item>,
201{
202    Chained(O::IntoIter, C::IntoIter),
203    Original(O::IntoIter),
204}
205
206impl<O, C> Iterator for ChainIf<O, C>
207where
208    O: IntoIterator,
209    C: IntoIterator<Item = O::Item>,
210{
211    type Item = O::Item;
212
213    fn next(&mut self) -> Option<Self::Item> {
214        match self {
215            ChainIf::Chained(original, chained) => original.next().or_else(|| chained.next()),
216            ChainIf::Original(original) => original.next(),
217        }
218    }
219}
220
221/// An iterator that conditionally chains together other iterators.
222///
223/// This `enum` is created by the [`IteratorExt::chain_if_else`] and
224/// [`IteratorExt::chain_if_else_with`] functions.
225///
226/// [`IteratorExt::chain_if_else`]: ./trait.IteratorExt.html#method.chain_if_else
227/// [`IteratorExt::chain_if_else_with`]: ./trait.IteratorExt.html#method.chain_if_else_with
228pub enum ChainIfElse<O, I, E>
229where
230    O: IntoIterator,
231    I: IntoIterator<Item = O::Item>,
232    E: IntoIterator<Item = O::Item>,
233{
234    If(O::IntoIter, I::IntoIter),
235    Else(O::IntoIter, E::IntoIter),
236}
237
238impl<O, I, E> Iterator for ChainIfElse<O, I, E>
239where
240    O: IntoIterator,
241    I: IntoIterator<Item = O::Item>,
242    E: IntoIterator<Item = O::Item>,
243{
244    type Item = O::Item;
245
246    fn next(&mut self) -> Option<Self::Item> {
247        match self {
248            ChainIfElse::If(original, other) => original.next().or_else(|| other.next()),
249            ChainIfElse::Else(original, other) => original.next().or_else(|| other.next()),
250        }
251    }
252}