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}