empty_fallback_chain/lib.rs
1//! [core-iter-chain]: `core::iter::Chain`
2//! [iterator-ext-method]: `IteratorExt::empty_fallback_chain`
3#![doc = include_str!("../README.md")]
4#![cfg_attr(not(test), no_std)]
5
6// TODO: in next major version, change the [`empty_fallback_chain`], [`maybe_empty_fallback_chain`],
7// and [`pre_empty_fallback_chain`] functions to use IntoIterator and make them non-const, to align
8// them with how std does free functions and make them not just glorified invokers of the constructor.
9
10/// Construct an [`EmptyFallbackChain`] iterator. See [`IteratorExt::empty_fallback_chain`] for
11/// more information.
12#[inline]
13pub fn empty_fallback_chain<A: IntoIterator, B: IntoIterator<Item = A::Item>>(
14 a: A,
15 b: B,
16) -> EmptyFallbackChain<A::IntoIter, B::IntoIter> {
17 EmptyFallbackChain::new(a.into_iter(), b.into_iter())
18}
19
20/// Construct an [`EmptyFallbackChain`] iterator with the second one optionally "pre-emptively made
21/// empty"
22///
23/// See [`IteratorExt::maybe_empty_fallback_chain`] for more information.
24#[inline]
25pub fn maybe_empty_fallback_chain<A: IntoIterator, B: IntoIterator<Item = A::Item>>(
26 a: A,
27 b: Option<B>,
28) -> EmptyFallbackChain<A::IntoIter, B::IntoIter> {
29 EmptyFallbackChain::new_with_pre_empty(Some(a.into_iter()), b.map(IntoIterator::into_iter))
30}
31
32/// Create an [`EmptyFallbackChain`] with (optionally) one or more of the iterators set to "empty"
33/// pre-emptively, by providing it as [`None`].
34///
35/// This is useful when creating conditional combinators as it prevents nested iterator type explosion, and the
36/// associated indirection.
37///
38/// See also: [`IteratorExt::maybe_empty_fallback_chain`]
39///
40/// ```rust
41/// use empty_fallback_chain as efc;
42///
43/// let o = efc::pre_empty_fallback_chain::<core::iter::Empty<usize>, _>(
44/// None,
45/// Some([3, 4, 5].into_iter())
46/// ).collect::<Vec<usize>>();
47/// assert_eq!(o, vec![3, 4, 5]);
48/// ```
49#[inline]
50pub fn pre_empty_fallback_chain<A: IntoIterator, B: IntoIterator<Item = A::Item>>(
51 a: Option<A>,
52 b: Option<B>,
53) -> EmptyFallbackChain<A::IntoIter, B::IntoIter> {
54 EmptyFallbackChain::new_with_pre_empty(
55 a.map(IntoIterator::into_iter),
56 b.map(IntoIterator::into_iter),
57 )
58}
59
60/// An iterator that links two iterators together, in a chain, if the first produces nothing.
61///
62/// This iterator is double ended - like [`core::iter::Chain`] - and behaves symmetrically in
63/// reverse - once you call either [`Iterator::next`] or [`DoubleEndedIterator::next_back`], the first
64/// iterator *in that direction* determines if the second iterator *in that direction* is
65/// preserved.
66///
67/// For more information, see [`IteratorExt::empty_fallback_chain`]
68#[derive(Debug, Clone)]
69#[must_use = "iterators are lazy and do nothing unless consumed"]
70pub struct EmptyFallbackChain<A, B> {
71 // This is like in `core::iter::Chain` - the Option values act to fuse the iterators.
72 //
73 // Destroying the second iterator is acheived by setting it to `None` in the and_then_or_clear
74 // function (mirroring the internal function of the same name inside rust stdlib).
75 //
76 // This also works (in reverse) for the double-ended case, with flipped behaviour
77 a: Option<A>,
78 b: Option<B>,
79}
80
81impl<A, B> EmptyFallbackChain<A, B> {
82 /// Construct a new [`EmptyFallbackChain`] from the two iterators.
83 #[inline]
84 pub const fn new(a: A, b: B) -> EmptyFallbackChain<A, B> {
85 Self {
86 a: Some(a),
87 b: Some(b),
88 }
89 }
90
91 /// Allow creating a new [`EmptyFallbackChain`] while implicitly allowing one or both of the
92 /// iterators to be pre-emptively considered "empty" by setting them to [`None`]. See
93 /// [`pre_empty_fallback_chain`] for more information.
94 #[inline]
95 pub const fn new_with_pre_empty(a: Option<A>, b: Option<B>) -> EmptyFallbackChain<A, B> {
96 Self { a, b }
97 }
98}
99
100/// Implementation of [`Iterator`] - takes significant work and code from [`core::iter::Chain`]'s
101/// implementation.
102impl<A, B> Iterator for EmptyFallbackChain<A, B>
103where
104 A: Iterator,
105 B: Iterator<Item = A::Item>,
106{
107 type Item = A::Item;
108
109 #[inline]
110 fn next(&mut self) -> Option<Self::Item> {
111 and_then_or_clear(&mut self.a, Iterator::next, || self.b = None)
112 .or_else(|| self.b.as_mut()?.next())
113 }
114
115 #[inline]
116 fn count(self) -> usize
117 where
118 Self: Sized,
119 {
120 let a_count = match self.a {
121 Some(a) => a.count(),
122 None => 0,
123 };
124 // `self.b` would get dumped anyway, and this function is consuming.
125 //
126 if a_count != 0 {
127 return a_count;
128 }
129 // If `self.a` was totally consumed but hadn't yet returned `None`, then on the
130 // first call, `self.b` would have been destroyed anyway, so continuing when a.count()
131 // is zero is still fine - there can't be an issue where:
132 // * `a` has cnt zero
133 // * `a` was not an empty iterator
134 // * `b` was not None
135 match self.b {
136 Some(b) => b.count(),
137 None => 0,
138 }
139 }
140
141 /*
142 * Not yet stable
143 fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
144 where
145 Self: Sized,
146 F: FnMut(B, Self::Item) -> R,
147 R: core::ops::Try<Output = B>, { Cannot use this because it's unstable
148
149 }
150 */
151
152 fn fold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
153 where
154 Self: Sized,
155 F: FnMut(Acc, Self::Item) -> Acc,
156 {
157 let mut had_element = false;
158 if let Some(a) = self.a {
159 acc = a.fold(acc, |acc, item| {
160 had_element = true;
161 f(acc, item)
162 });
163 }
164 if had_element {
165 // No need to reset b - this function consumes self
166 return acc;
167 }
168
169 // Once again, it's fine to exclusively depend on `b` being `None` to determine if `a`
170 // ever had any values - even if at the point of calling this function it didn't, it
171 // should only ever have been advanced by this iterator in the past and hence `b` would
172 // have been set to `None`
173 if let Some(b) = self.b {
174 acc = b.fold(acc, f);
175 }
176 acc
177 }
178
179 /*
180 #[inline]
181 fn advance_by(&mut self, mut n: usize) -> Result<(), core::num::NonZero<usize>> {
182 if let Some(ref mut a) = self.a {
183 n = match (a.advance_by(n), n) {
184 // `a` actually did not advance at all, and there were no elements.
185 (Ok(()), 0) => return Ok(()),
186 // `a` failed to advance any amount, meaning it was an empty iterator (if it wasn't
187 // earlier, then `b` would have already been destroyed and that's ok).
188 // We destroy `a` for cleanup and fusion reasons.
189 (Err(k), tried) if tried == k.get() => {
190 self.a = None;
191 tried
192 }
193 (Ok(()), a_advanced_by) => {
194 // `a` finished (though not actually "completed completed") with a
195 // nonzero advancement count. This means it isn't empty, and we need to
196 // destroy `b`.
197 self.b = None;
198 return Ok(());
199 },
200 // `a` failed to complete, but it did manage to advance. This still means that `a`
201 // is nonempty, and `b` should be destroyed. This also means *we* cannot advance
202 // further, so should forward up the error (also destroy `a` again.
203 (Err(k), tried) => {
204 self.a = None;
205 self.b = None;
206 return Err(k)
207 }
208 };
209 }
210
211 if let Some(ref mut b) = self.b {
212 return b.advance_by(n);
213 }
214
215 // Nothing could actually do anything with `advance_by`, as we have no more iterators,
216 // so make it all error-y anyway if the advancement is not zero
217 NonZero::new(n).map_or(Ok(()), Err)
218 }
219 Cannot currently implement because unstable lol
220 */
221
222 /* Can't easily do any "proper" better-than-default implementation without using
223 * unstable features. TODO: still implement this
224 fn nth(&mut self, n: usize) -> Option<Self::Item> {
225
226 }
227 */
228
229 #[inline]
230 fn find<P>(&mut self, mut predicate: P) -> Option<Self::Item>
231 where
232 Self: Sized,
233 P: FnMut(&Self::Item) -> bool,
234 {
235 let mut did_have_elements = false;
236 // Run through `a`. We've injected our boolean into the predicate, so we can determine
237 // if `a` ever called it (which it would have to do at least once if it had any elements)
238 let a_found = and_then_or_clear(
239 &mut self.a,
240 |a| {
241 a.find(|v| {
242 did_have_elements = true;
243 predicate(v)
244 })
245 },
246 || {},
247 );
248 if did_have_elements {
249 self.b = None;
250 return a_found;
251 }
252 // Run find on `b`. `b` would be `None` if `a` had ever had any elements
253 self.b.as_mut()?.find(predicate)
254 }
255
256 #[inline]
257 fn last(self) -> Option<Self::Item>
258 where
259 Self: Sized,
260 {
261 // We get the last of `a`, if `a` is not None
262 let a_last = self.a.and_then(Iterator::last);
263 // If this was Some, then `b` must be cleared. If it was `None`, then `b` was either
264 // cleared earlier (by `a` not being empty, and being run through), or `a` is empty and `b`
265 // is set to Some and can be used directly.
266 //
267 // We are actually consuming self, so no need to update anything.
268 if a_last.is_some() {
269 return a_last;
270 }
271
272 self.b.and_then(Iterator::last)
273 }
274
275 #[inline]
276 fn size_hint(&self) -> (usize, Option<usize>) {
277 match (&self.a, &self.b) {
278 (None, None) => (0, Some(0)),
279 (None, Some(b)) => b.size_hint(),
280 (Some(a), None) => a.size_hint(),
281 (Some(a), Some(b)) => {
282 // If `a` has a nonzero lower bound, then we know `b` will be removed.
283 // If `a` has a zero upper bound, then we know `b` is all that will be iterated
284 // If `b` has a zero upper bound, then we know `a` is all that will be iterated
285 // If `b` has a nonzero lower bound, then we know that if iterating backwards, `a`
286 // will not be iterated. However, size_hint is for forward iteration.
287 // Other than this, we can guaruntee that the lower bound is one of the two (we can
288 // be safe by picking minimum), and the upper bound is one of the two (we can be
289 // safe by picking maximum, or `None` if either of them have `None`)
290 match (a.size_hint(), b.size_hint()) {
291 (a_size_hint @ (1.., _), _) => a_size_hint,
292 ((_, Some(0)), b_size_hint) => b_size_hint,
293 (a_size_hint, (_, Some(0))) => a_size_hint,
294 (
295 (a_lower_bound, maybe_a_upper_bound),
296 (b_lower_bound, maybe_b_upper_bound),
297 ) => {
298 let maybe_upper_bound = match (maybe_a_upper_bound, maybe_b_upper_bound) {
299 (Some(a_upper), Some(b_upper)) => Some(a_upper.max(b_upper)),
300 _ => None,
301 };
302 let lower_bound = a_lower_bound.min(b_lower_bound);
303 (lower_bound, maybe_upper_bound)
304 }
305 }
306 }
307 }
308 }
309}
310
311impl<A, B> DoubleEndedIterator for EmptyFallbackChain<A, B>
312where
313 A: DoubleEndedIterator,
314 B: DoubleEndedIterator<Item = A::Item>,
315{
316 #[inline]
317 fn next_back(&mut self) -> Option<Self::Item> {
318 // Implement the same as `next`, but flip which thing gets called and reset.
319 and_then_or_clear(&mut self.b, DoubleEndedIterator::next_back, || {
320 self.a = None;
321 })
322 .or_else(|| self.a.as_mut()?.next_back())
323 }
324
325 /* unstable
326 fn advance_back_by(&mut self, n: usize) -> Result<(), core::num::NonZero<usize>> {}
327 */
328
329 /* Cannot easily implement without unstable advance_back_by - TODO: implement
330 fn nth_back(&mut self, n: usize) -> Option<Self::Item> {
331
332 }*/
333
334 #[inline]
335 fn rfind<P>(&mut self, mut predicate: P) -> Option<Self::Item>
336 where
337 Self: Sized,
338 P: FnMut(&Self::Item) -> bool,
339 {
340 let mut had_any_elements = false;
341 // Run the find function, injecting into the predicate - which must be called
342 // at least once for any non-empty iterator.
343 let b_found = and_then_or_clear(
344 &mut self.b,
345 |b| {
346 b.rfind(|v| {
347 had_any_elements = true;
348 predicate(v)
349 })
350 },
351 || {},
352 );
353 if had_any_elements {
354 self.a = None;
355 return b_found;
356 }
357 self.a.as_mut()?.rfind(predicate)
358 }
359
360 /* unstable
361 fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
362 where
363 Self: Sized,
364 F: FnMut(B, Self::Item) -> R,
365 R: core::ops::Try<Output = B>, {
366
367 }
368 */
369
370 fn rfold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
371 where
372 Self: Sized,
373 F: FnMut(Acc, Self::Item) -> Acc,
374 {
375 // This function is consuming, so we don't need to worry much about disposing of
376 // anything, just returning when needed :)
377 let mut had_any_elements = false;
378 if let Some(b) = self.b {
379 acc = b.rfold(acc, |acc, item| {
380 had_any_elements = true;
381 f(acc, item)
382 });
383 }
384 if had_any_elements {
385 // Consuming function, no need to deinit `a`
386 return acc;
387 }
388
389 if let Some(a) = self.a {
390 acc = a.rfold(acc, f);
391 }
392 acc
393 }
394}
395
396/// Execute the given function on the option, clearing the option if the output from the function
397/// was [`None`]. Also allows providing a callback to run if the output of `f` was `Some`.
398///
399/// This is pretty much identical to the function inside the standard library, except with added
400/// callback.
401#[inline]
402fn and_then_or_clear<T, U>(
403 resetting_input: &mut Option<T>,
404 with_some_input: impl FnOnce(&mut T) -> Option<U>,
405 on_some: impl FnOnce(),
406) -> Option<U> {
407 let output = with_some_input(resetting_input.as_mut()?);
408 if output.is_none() {
409 *resetting_input = None;
410 } else {
411 on_some();
412 };
413 output
414}
415
416/// Trait for extending [`Iterator`]s with methods to create [`EmptyFallbackChain`] iterators.
417pub trait IteratorExt: Iterator {
418 /// Takes two iterators and creates a new iterator that runs through the second only if the
419 /// first produces no output. Can take anything implementing [`IntoIterator`] as a second
420 /// argument (as long as it produces an iterator over the same type).
421 ///
422 /// `empty_fallback_chain()` will return a new iterator which will iterate over this (first) iterator - if
423 /// it produces any values, then the second iterator is dropped. However, if the first iterator doesn't
424 /// produce any values, then the second iterator is iterated over instead (i.e. "as a
425 /// fallback").
426 ///
427 /// In other words, it links two iterators in a chain, but only if the first is empty.
428 ///
429 /// This also applies in reverse direction when iterating backwards (i.e. with [`DoubleEndedIterator::next_back`],
430 /// the second iterator is attempted to be iterated backwards first - only if nothing is
431 /// emitted, is the first iterator then iterated backwards instead, else the first iterator is simply dropped).
432 ///
433 /// # Examples
434 ///
435 /// Basic usage:
436 /// ```
437 /// use empty_fallback_chain::prelude::*;
438 /// let higher_priority = [1, 2, 3];
439 /// let lower_priority = [4, 5, 6];
440 ///
441 /// let iter = higher_priority.into_iter().empty_fallback_chain(lower_priority.into_iter());
442 /// assert_eq!(iter.collect::<Vec<_>>(), vec![1, 2, 3]);
443 /// ```
444 ///
445 /// The major feature of [`EmptyFallbackChain`] is that if the first iterator produces
446 /// no values, then the second iterator will be used instead.
447 /// ```
448 /// use empty_fallback_chain::IteratorExt as _;
449 ///
450 /// let higher_priority = [1, 3, 5];
451 /// let lower_priority = [10, 11, 78];
452 ///
453 /// /// Filter for even numbers - no data in the higher priority iterator matches this,
454 /// /// so when the filtered version is used as the first half of an `EmptyFallbackChain`,
455 /// /// the "fallback" iterator is what's used.
456 /// fn even(v: &u32) -> bool {
457 /// v % 2 == 0
458 /// }
459 ///
460 /// let iter = higher_priority.into_iter().filter(even)
461 /// .empty_fallback_chain(lower_priority.into_iter());
462 /// assert_eq!(iter.collect::<Vec<_>>(), vec![10, 11, 78]);
463 /// ```
464 ///
465 /// If the higher priority iterator produces *any* values, then the lower priority iterator is
466 /// never used. For example, with a filter that doesn't remove all of the higher-priority
467 /// information:
468 /// ```
469 /// use empty_fallback_chain::prelude::*;
470 ///
471 /// let higher_priority = [1, 3, 5];
472 /// let lower_priority = [10, 11, 78];
473 ///
474 /// fn incomplete_higher_filter(v: &u32) -> bool {
475 /// *v != 3
476 /// }
477 ///
478 /// let iter = higher_priority.into_iter().filter(incomplete_higher_filter)
479 /// .empty_fallback_chain(lower_priority.into_iter());
480 /// assert_eq!(iter.collect::<Vec<_>>(), vec![1, 5]);
481 /// ```
482 ///
483 /// This can be used to create incredibly powerful, lazily evaluated, fallback systems.
484 /// If you use multiple [`EmptyFallbackChain`] in sequence, you can create a sort of
485 /// "iterator priority" construction.
486 /// ```
487 /// use empty_fallback_chain::prelude::*;
488 ///
489 /// #[derive(Debug, Clone, PartialEq, Eq, Hash)]
490 /// pub struct Contact {
491 /// pub name: &'static str,
492 /// pub email: &'static str,
493 /// pub pronouns: &'static str
494 /// }
495 ///
496 ///# impl Contact {
497 ///# /// Just example - You'd probably use owned, interned, or referenced strings
498 ///# /// here.
499 ///# pub const fn new(
500 ///# name: &'static str,
501 ///# email: &'static str,
502 ///# pronouns: &'static str
503 ///# ) -> Self {
504 ///# Self { name, email, pronouns }
505 ///# }
506 ///# }
507 /// // Example conditions
508 /// fn is_tuesday() -> bool { true }
509 /// fn is_wednesday() -> bool { false }
510 /// fn is_weekend() -> bool { false }
511 ///
512 /// use Contact as C;
513 /// use core::iter as iter;
514 ///
515 /// const bob: C = C::new("Bob Angie", "the-eponymous-bob@example.com", "he/him");
516 /// const tracey: C = C::new("Tracy Mill", "tracy-mill@corpo.example.com", "she/her");
517 /// const alan: C = C::new("Alan", "alanspace@example.com", "he/him");
518 /// const matriss: C = C::new("Matriss Karisle", "matriss@example.com", "they/them");
519 /// const charlie: C = C::new("Charlie Stone", "charlie-charlie@example.com", "she/her");
520 /// const harri: C = C::new("Harri", "harri-up@example.com", "they/she");
521 /// const mel: C = C::new("Mel", "mel@corpo.example.com", "she/her");
522 /// const troy: C = C::new("Troy", "helenofcity@example.com", "he/him");
523 ///
524 /// // Define the contact lists as functions producing iterators, in order of preference.
525 /// // In reality, you'd use some better means of determining availability, but the principle
526 /// // of using fallbacks is sound, and can be used for many scenarios
527 ///
528 /// fn emergency_contacts() -> impl Iterator<Item = Contact> {
529 /// iter::empty()
530 /// .chain(iter::once(troy).filter(|_| !is_tuesday() && !is_weekend()))
531 /// .chain(iter::once(charlie).filter(|_| !is_weekend()))
532 /// .chain([bob, matriss, harri].into_iter().filter(|_| !is_tuesday() &&
533 /// !is_wednesday()))
534 /// }
535 ///
536 /// fn distant_family_contacts() -> impl Iterator<Item = Contact> {
537 /// // fill in here
538 ///# iter::empty()
539 ///# .chain(iter::once(tracey).filter(|_| !is_weekend() && !is_tuesday()))
540 /// }
541 ///
542 /// fn corpo_contacts() -> impl Iterator<Item = Contact> {
543 /// // fill in here
544 ///# iter::empty()
545 ///# .chain(iter::once(mel).filter(|_| !is_weekend()))
546 ///# .chain(iter::once(tracey))
547 /// }
548 ///
549 /// fn friendly_evening_contacts() -> impl Iterator<Item = Contact> {
550 /// // fill in here
551 ///# iter::empty()
552 ///# .chain([matriss, harri, mel].into_iter()
553 ///# .filter(|_| !is_weekend() && !is_tuesday()))
554 ///# .chain([alan, troy, charlie].into_iter()
555 ///# .filter(|_| !is_weekend() && !is_wednesday()))
556 /// }
557 ///
558 /// // Then, build contact scenarios, using `empty_fallback_chain`
559 /// // If there are no contacts available for emergency situations, then
560 /// // this will iterate for contacts who can just be messaged for a "friendly evening".
561 /// // If that fails, then it will iterate over all distant family contacts.
562 /// fn i_have_an_emergency() -> impl Iterator<Item = Contact> {
563 /// emergency_contacts()
564 /// .empty_fallback_chain(friendly_evening_contacts())
565 /// .empty_fallback_chain(distant_family_contacts())
566 ///
567 /// }
568 ///
569 /// fn i_want_a_friendly_time() -> impl Iterator<Item = Contact> {
570 /// friendly_evening_contacts()
571 /// .empty_fallback_chain(distant_family_contacts())
572 /// .empty_fallback_chain(corpo_contacts())
573 /// }
574 ///
575 /// fn i_am_having_an_existential_crisis() -> impl Iterator<Item = Contact> {
576 /// // fill in here
577 ///# distant_family_contacts()
578 ///# .empty_fallback_chain(friendly_evening_contacts())
579 /// }
580 /// ```
581 #[inline]
582 fn empty_fallback_chain<U>(self, other: U) -> EmptyFallbackChain<Self, U::IntoIter>
583 where
584 Self: Sized,
585 U: IntoIterator<Item = Self::Item>,
586 {
587 EmptyFallbackChain::new(self, other.into_iter())
588 }
589
590 /// Provide an empty fallback chain to this iterator, but only optionally so.
591 ///
592 /// Should it be a common case that you wish to only provide an empty fallback sometimes but
593 /// not others, then this function will let you efficiently create an [`EmptyFallbackChain`]
594 /// where the second iterator can be set to empty easily by just passing [`None`], equivalent
595 /// to there being no fallback, or a fallback which produces no iterator items.
596 ///
597 /// This creates less type-nesting and less indirection than using some other mechanisms to
598 /// "optionally" iterate something. Note that should you wish to provide [`None`] directly as a
599 /// parameter, you may need to use the `::<>` turbofish syntax to manage type deductions - use
600 /// any iterator that makes sense in the context here, as you will be setting it to [`None`]
601 /// anyway.
602 ///
603 /// # Basic Illustration
604 /// ```rust
605 /// use empty_fallback_chain::IteratorExt as _;
606 /// use core::iter::Empty;
607 ///
608 /// fn maybe_empty_fb<A: IntoIterator, B: IntoIterator<Item = A::Item>>(a: A, b: Option<B>) -> Vec<A::Item> {
609 /// a.into_iter().maybe_empty_fallback_chain(b.map(IntoIterator::into_iter)).collect::<Vec<_>>()
610 /// }
611 ///
612 /// assert_eq!(maybe_empty_fb([1,2,3], Some([4, 5, 6])), vec![1, 2, 3]);
613 /// assert_eq!(maybe_empty_fb::<_, Empty<usize>>([1,2,3], None), vec![1, 2, 3]);
614 /// assert_eq!(maybe_empty_fb([], Some([4, 5, 6])), vec![4, 5, 6]);
615 /// assert_eq!(maybe_empty_fb::<_, Empty<usize>>([], None), vec![]);
616 /// ```
617 ///
618 /// # Conditionally Providing Fallback
619 /// ```rust
620 /// use empty_fallback_chain::{IteratorExt as _, EmptyFallbackChain};
621 /// use core::iter;
622 ///
623 /// fn apply_fallback_conditionally(
624 /// a: impl IntoIterator<Item = usize>,
625 /// do_fallback: bool
626 /// ) -> impl Iterator<Item = usize> {
627 /// if do_fallback {
628 /// a.into_iter().empty_fallback_chain([3, 4, 5].iter().copied())
629 /// } else {
630 /// // Note here that we need to provide some help with deduction by providing the
631 /// // outermost iterator of the chain as a hint, because `maybe_empty_fallback_chain`
632 /// // takes an `IntoIterator` as it's parameter and then uses the associated
633 /// // `IntoIter` type, rather than only taking `Iterator`s
634 /// //
635 /// // Another way to solve this problem is to only use one call to
636 /// // `maybe_empty_fallback_chain`, and unify the `Some` and `None` cases into a
637 /// // single `Option` beforehand.
638 /// a.into_iter().maybe_empty_fallback_chain::<iter::Copied<_>>(None)
639 /// }
640 /// }
641 ///
642 /// assert_eq!(apply_fallback_conditionally([1, 2], true).collect::<Vec<_>>(), vec![1, 2]);
643 /// assert_eq!(apply_fallback_conditionally([], true).collect::<Vec<_>>(), vec![3, 4, 5]);
644 /// assert_eq!(apply_fallback_conditionally([1, 2], false).collect::<Vec<_>>(), vec![1, 2]);
645 /// assert_eq!(apply_fallback_conditionally([], false).collect::<Vec<_>>(), vec![]);
646 /// ```
647 #[inline]
648 fn maybe_empty_fallback_chain<U>(
649 self,
650 other: Option<U>,
651 ) -> EmptyFallbackChain<Self, U::IntoIter>
652 where
653 Self: Sized,
654 U: IntoIterator<Item = Self::Item>,
655 {
656 EmptyFallbackChain::new_with_pre_empty(Some(self), other.map(IntoIterator::into_iter))
657 }
658}
659
660impl<T: Iterator + ?Sized> IteratorExt for T {}
661
662pub mod prelude {
663 pub use super::IteratorExt as _;
664}
665
666#[cfg(test)]
667mod tests {
668 use core::iter;
669
670 fn none_iter<T: Iterator>() -> iter::Flatten<core::option::IntoIter<T>> {
671 None.into_iter().flatten()
672 }
673
674 fn some_iter<T: Iterator>(t: T) -> iter::Flatten<core::option::IntoIter<T>> {
675 Some(t).into_iter().flatten()
676 }
677
678 use super::*;
679 /// Make the "first" iterator to compose [`make_conditional_iter`]
680 fn make_first_iter() -> impl DoubleEndedIterator<Item = u32> + Clone {
681 [0].into_iter()
682 }
683
684 /// Make the "second" iterator to compose [`make_conditional_iter`]
685 fn make_second_iter() -> impl DoubleEndedIterator<Item = u32> + Clone {
686 [10, 11].into_iter()
687 }
688
689 /// Make the "third" iterator to compose [`make_conditional_iter`]
690 fn make_third_iter() -> impl DoubleEndedIterator<Item = u32> + Clone {
691 [20, 21, 22, 23, 24, 25].into_iter()
692 }
693
694 /// All values that might be emitted from an iterator made by [`make_conditional_iter`]
695 /// Not intended for direct comparison, but just for getting all the values.
696 fn make_all_values_iter() -> impl Iterator<Item = u32> + Clone {
697 make_first_iter()
698 .chain(make_second_iter())
699 .chain(make_third_iter())
700 }
701
702 /// Get a value not present in any possible [`make_conditional_iter`] combination.
703 fn non_present_value() -> u32 {
704 make_all_values_iter().max().unwrap() + 1
705 }
706
707 /// Make an "equivalent" known-good iterator for the given 3-boolean configuration,
708 /// only for the forward-order though
709 fn make_ideal_equivalent_iter_for(
710 include_values: [bool; 3],
711 ) -> impl Iterator<Item = u32> + Clone {
712 match include_values {
713 [true, _, _] => some_iter(make_first_iter())
714 .chain(none_iter())
715 .chain(none_iter()),
716 [false, true, _] => none_iter()
717 .chain(some_iter(make_second_iter()))
718 .chain(none_iter()),
719 [false, false, true] => none_iter()
720 .chain(none_iter())
721 .chain(some_iter(make_third_iter())),
722 [false, false, false] => none_iter().chain(none_iter()).chain(none_iter()),
723 }
724 }
725
726 /// Make an "equivalent" known-good (for the reverse direction) [`DoubleEndedIterator`] for the
727 /// given 3-boolean configuration, only for the reverse order functions though.
728 fn make_ideal_equivalent_reverse_iter_for(
729 include_values: [bool; 3],
730 ) -> impl DoubleEndedIterator<Item = u32> + Clone {
731 match include_values {
732 [_, _, true] => none_iter()
733 .chain(none_iter())
734 .chain(some_iter(make_third_iter())),
735 [_, true, false] => none_iter()
736 .chain(some_iter(make_second_iter()))
737 .chain(none_iter()),
738 [true, false, false] => some_iter(make_first_iter())
739 .chain(none_iter())
740 .chain(none_iter()),
741 [false, false, false] => none_iter().chain(none_iter()).chain(none_iter()),
742 }
743 }
744
745 /// Create a 3 element iterator chained with [`EmptyFallbackChain`] and where each given
746 /// subiterator either has values or does not depending on the booleans.
747 ///
748 /// The values in question are, if present, for the iterators in order:
749 /// * `[0]`,
750 /// * `[10, 11]`
751 /// * `[20, 21, 22, 23, 24, 25]`
752 fn make_conditional_iter(
753 include_values: [bool; 3],
754 ) -> impl DoubleEndedIterator<Item = u32> + Clone {
755 let first_iter = make_first_iter().filter(move |_| include_values[0]);
756 let second_iter = make_second_iter().filter(move |_| include_values[1]);
757 let third_iter = make_third_iter().filter(move |_| include_values[2]);
758 first_iter
759 .empty_fallback_chain(second_iter)
760 .empty_fallback_chain(third_iter)
761 }
762
763 /// Compare the functionality of a pair of iterators that should be "equivalent"
764 /// This means basic things, but also the more advanced iterator methods.
765 ///
766 /// Note that this will not compare [`DoubleEndedIterator`] methods - because the equivalent
767 /// "known good" iterator might be different for the opposite direction.
768 fn compare_iters(
769 known_good: impl Iterator<Item = u32> + Clone,
770 to_test: impl Iterator<Item = u32> + Clone,
771 ) {
772 // Test contents
773 assert_eq!(
774 known_good.clone().collect::<Vec<_>>(),
775 to_test.clone().collect::<Vec<_>>()
776 );
777
778 assert_eq!(known_good.clone().count(), to_test.clone().count());
779
780 assert_eq!(
781 known_good.clone().fold(3, |a, b| a + b),
782 to_test.clone().fold(3, |a, b| a + b)
783 );
784
785 for possible_value in make_all_values_iter().chain(iter::once(non_present_value())) {
786 assert_eq!(
787 known_good.clone().find(|v| *v == possible_value),
788 to_test.clone().find(|v| *v == possible_value)
789 )
790 }
791
792 assert_eq!(known_good.last(), to_test.last());
793 }
794
795 /// Compare specifically the reverse-based functionality of a pair of double ended iterators.
796 /// This is separated out from [`compare_iters`] because of divergent inverse behaviour which
797 /// means there is a different "ideal" model for the inverse mode.
798 fn compare_inverse_iters(
799 known_good: impl DoubleEndedIterator<Item = u32> + Clone,
800 to_test: impl DoubleEndedIterator<Item = u32> + Clone,
801 ) {
802 // Test inverse contents.
803 assert_eq!(
804 known_good.clone().rev().collect::<Vec<_>>(),
805 to_test.clone().rev().collect::<Vec<_>>()
806 );
807
808 // Test reverse-find
809 for possible_value in make_all_values_iter().chain(iter::once(non_present_value())) {
810 assert_eq!(
811 known_good.clone().rfind(|v| *v == possible_value),
812 to_test.clone().rfind(|v| *v == possible_value)
813 );
814 }
815
816 // Test rfold
817 assert_eq!(
818 known_good.rfold(3, |acc, v| acc + v),
819 to_test.rfold(3, |acc, v| acc + v)
820 );
821 }
822
823 /// Run tests for the [`make_conditional_iter`] generated by the given boolean triplet, against
824 /// the "ideal" iterator that it should be equivalent to if [`EmptyFallbackChain`] works
825 /// correctly.
826 fn compare_boolean_made_iters(include_values: [bool; 3]) {
827 compare_iters(
828 make_ideal_equivalent_iter_for(include_values),
829 make_conditional_iter(include_values),
830 );
831 }
832
833 /// Run tests for the [`make_conditional_iter`] generated by the given boolean triplet, against
834 /// the "ideal" double ended iterator that it's inverse "double-ended" methods should be
835 /// equivalent to if [`EmptyFallbackChain`] works correctly.
836 fn compare_boolean_made_inverse_iters(include_values: [bool; 3]) {
837 compare_inverse_iters(
838 make_ideal_equivalent_reverse_iter_for(include_values),
839 make_conditional_iter(include_values),
840 )
841 }
842
843 #[test]
844 fn chained_priority_basics() {
845 for i0 in [false, true] {
846 for i1 in [false, true] {
847 for i2 in [false, true] {
848 compare_boolean_made_iters([i0, i1, i2]);
849 compare_boolean_made_inverse_iters([i0, i1, i2]);
850 }
851 }
852 }
853 }
854}
855
856// This Source Code Form is subject to the terms of the Mozilla Public
857// License, v. 2.0. If a copy of the MPL was not distributed with this
858// file, You can obtain one at http://mozilla.org/MPL/2.0/.