empty_fallback_chain/
lib.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
//! [core-iter-chain]: `core::iter::Chain`
//! [iterator-ext-method]: `IteratorExt::empty_fallback_chain`
#![doc = include_str!("../README.md")]
#![cfg_attr(not(test), no_std)]

/// Construct an [`EmptyFallbackChain`] iterator. See [`IteratorExt::empty_fallback_chain`] for
/// more information. 
#[inline]
pub const fn empty_fallback_chain<A, B>(a: A, b: B) -> EmptyFallbackChain<A, B> {
    EmptyFallbackChain::new(a, b)
}

/// An iterator that links two iterators together, in a chain, if the first produces nothing.
///
/// This iterator is double ended - like [`core::iter::Chain`] - and behaves symmetrically in
/// reverse - once you call either [`Iterator::next`] or [`DoubleEndedIterator::next_back`], the first
/// iterator *in that direction* determines if the second iterator *in that direction* is
/// preserved.
///
/// For more information, see [`IteratorExt::empty_fallback_chain`]
#[derive(Debug, Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct EmptyFallbackChain<A, B> {
    // This is like in `core::iter::Chain` - the Option values act to fuse the iterators.
    //
    // Destroying the second iterator is acheived by setting it to `None` in the and_then_or_clear
    // function (mirroring the internal function of the same name inside rust stdlib).
    //
    // This also works (in reverse) for the double-ended case, with flipped behaviour
    a: Option<A>,
    b: Option<B>,
}

impl<A, B> EmptyFallbackChain<A, B> {
    /// Construct a new [`EmptyFallbackChain`] from the two iterators. 
    #[inline]
    pub const fn new(a: A, b: B) -> EmptyFallbackChain<A, B> {
        Self {
            a: Some(a),
            b: Some(b),
        }
    }
}

/// Implementation of [`Iterator`] - takes significant work and code from [`core::iter::Chain`]'s
/// implementation.
impl<A, B> Iterator for EmptyFallbackChain<A, B>
where
    A: Iterator,
    B: Iterator<Item = A::Item>,
{
    type Item = A::Item;

    #[inline]
    fn next(&mut self) -> Option<Self::Item> {
        and_then_or_clear(&mut self.a, Iterator::next, || self.b = None)
            .or_else(|| self.b.as_mut()?.next())
    }

    #[inline]
    fn count(self) -> usize
    where
        Self: Sized,
    {
        let a_count = match self.a {
            Some(a) => a.count(),
            None => 0,
        };
        // `self.b` would get dumped anyway, and this function is consuming.
        //
        if a_count != 0 {
            return a_count;
        }
        // If `self.a` was totally consumed but hadn't yet returned `None`, then on the
        // first call, `self.b` would have been destroyed anyway, so continuing when a.count()
        // is zero is still fine - there can't be an issue where:
        // * `a` has cnt zero
        // * `a` was not an empty iterator
        // * `b` was not None
        match self.b {
            Some(b) => b.count(),
            None => 0,
        }
    }

    /*
     * Not yet stable
    fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
        where
            Self: Sized,
            F: FnMut(B, Self::Item) -> R,
            R: core::ops::Try<Output = B>, { Cannot use this because it's unstable

    }
    */

    fn fold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
    where
        Self: Sized,
        F: FnMut(Acc, Self::Item) -> Acc,
    {
        let mut had_element = false;
        if let Some(a) = self.a {
            acc = a.fold(acc, |acc, item| {
                had_element = true;
                f(acc, item)
            });
        }
        if had_element {
            // No need to reset b - this function consumes self
            return acc;
        }

        // Once again, it's fine to exclusively depend on `b` being `None` to determine if `a`
        // ever had any values - even if at the point of calling this function it didn't, it
        // should only ever have been advanced by this iterator in the past and hence `b` would
        // have been set to `None`
        if let Some(b) = self.b {
            acc = b.fold(acc, f);
        }
        acc
    }

    /*
    #[inline]
    fn advance_by(&mut self, mut n: usize) -> Result<(), core::num::NonZero<usize>> {
        if let Some(ref mut a) = self.a {
            n = match (a.advance_by(n), n) {
                // `a` actually did not advance at all, and there were no elements.
                (Ok(()), 0) => return Ok(()),
                // `a` failed to advance any amount, meaning it was an empty iterator (if it wasn't
                // earlier, then `b` would have already been destroyed and that's ok).
                // We destroy `a` for cleanup and fusion reasons.
                (Err(k), tried) if tried == k.get() => {
                    self.a = None;
                    tried
                }
                (Ok(()), a_advanced_by) => {
                    // `a` finished (though not actually "completed completed") with a
                    // nonzero advancement count. This means it isn't empty, and we need to
                    // destroy `b`.
                    self.b = None;
                    return Ok(());
                },
                // `a` failed to complete, but it did manage to advance. This still means that `a`
                // is nonempty, and `b` should be destroyed. This also means *we* cannot advance
                // further, so should forward up the error (also destroy `a` again.
                (Err(k), tried) => {
                    self.a = None;
                    self.b = None;
                    return Err(k)
                }
            };
        }

        if let Some(ref mut b) = self.b {
            return b.advance_by(n);
        }

        // Nothing could actually do anything with `advance_by`, as we have no more iterators,
        // so make it all error-y anyway if the advancement is not zero
        NonZero::new(n).map_or(Ok(()), Err)
    }
    Cannot currently implement because unstable lol
    */

    /* Can't easily do any "proper" better-than-default implementation without using
     * unstable features. TODO: still implement this
    fn nth(&mut self, n: usize) -> Option<Self::Item> {

    }
    */

    #[inline]
    fn find<P>(&mut self, mut predicate: P) -> Option<Self::Item>
    where
        Self: Sized,
        P: FnMut(&Self::Item) -> bool,
    {
        let mut did_have_elements = false;
        // Run through `a`. We've injected our boolean into the predicate, so we can determine
        // if `a` ever called it (which it would have to do at least once if it had any elements)
        let a_found = and_then_or_clear(
            &mut self.a,
            |a| {
                a.find(|v| {
                    did_have_elements = true;
                    predicate(v)
                })
            },
            || {},
        );
        if did_have_elements {
            self.b = None;
            return a_found;
        }
        // Run find on `b`. `b` would be `None` if `a` had ever had any elements
        self.b.as_mut()?.find(predicate)
    }

    #[inline]
    fn last(self) -> Option<Self::Item>
    where
        Self: Sized,
    {
        // We get the last of `a`, if `a` is not None
        let a_last = self.a.and_then(Iterator::last);
        // If this was Some, then `b` must be cleared. If it was `None`, then `b` was either
        // cleared earlier (by `a` not being empty, and being run through), or `a` is empty and `b`
        // is set to Some and can be used directly.
        //
        // We are actually consuming self, so no need to update anything.
        if a_last.is_some() {
            return a_last;
        }

        self.b.and_then(Iterator::last)
    }

    #[inline]
    fn size_hint(&self) -> (usize, Option<usize>) {
        match (&self.a, &self.b) {
            (None, None) => (0, Some(0)),
            (None, Some(b)) => b.size_hint(),
            (Some(a), None) => a.size_hint(),
            (Some(a), Some(b)) => {
                // If `a` has a nonzero lower bound, then we know `b` will be removed.
                // If `a` has a zero upper bound, then we know `b` is all that will be iterated
                // If `b` has a zero upper bound, then we know `a` is all that will be iterated
                // If `b` has a nonzero lower bound, then we know that if iterating backwards, `a`
                // will not be iterated. However, size_hint is for forward iteration.
                // Other than this, we can guaruntee that the lower bound is one of the two (we can
                // be safe by picking minimum), and the upper bound is one of the two (we can be
                // safe by picking maximum, or `None` if either of them have `None`)
                match (a.size_hint(), b.size_hint()) {
                    (a_size_hint @ (1.., _), _) => a_size_hint,
                    ((_, Some(0)), b_size_hint) => b_size_hint,
                    (a_size_hint, (_, Some(0))) => a_size_hint,
                    (
                        (a_lower_bound, maybe_a_upper_bound),
                        (b_lower_bound, maybe_b_upper_bound),
                    ) => {
                        let maybe_upper_bound = match (maybe_a_upper_bound, maybe_b_upper_bound) {
                            (Some(a_upper), Some(b_upper)) => Some(a_upper.max(b_upper)),
                            _ => None,
                        };
                        let lower_bound = a_lower_bound.min(b_lower_bound);
                        (lower_bound, maybe_upper_bound)
                    }
                }
            }
        }
    }
}

impl<A, B> DoubleEndedIterator for EmptyFallbackChain<A, B>
where
    A: DoubleEndedIterator,
    B: DoubleEndedIterator<Item = A::Item>,
{
    #[inline]
    fn next_back(&mut self) -> Option<Self::Item> {
        // Implement the same as `next`, but flip which thing gets called and reset.
        and_then_or_clear(&mut self.b, DoubleEndedIterator::next_back, || {
            self.a = None;
        })
        .or_else(|| self.a.as_mut()?.next_back())
    }

    /* unstable
    fn advance_back_by(&mut self, n: usize) -> Result<(), core::num::NonZero<usize>> {}
    */

    /* Cannot easily implement without unstable advance_back_by - TODO: implement
    fn nth_back(&mut self, n: usize) -> Option<Self::Item> {

    }*/

    #[inline]
    fn rfind<P>(&mut self, mut predicate: P) -> Option<Self::Item>
    where
        Self: Sized,
        P: FnMut(&Self::Item) -> bool,
    {
        let mut had_any_elements = false;
        // Run the find function, injecting into the predicate - which must be called
        // at least once for any non-empty iterator.
        let b_found = and_then_or_clear(
            &mut self.b,
            |b| {
                b.rfind(|v| {
                    had_any_elements = true;
                    predicate(v)
                })
            },
            || {},
        );
        if had_any_elements {
            self.a = None;
            return b_found;
        }
        self.a.as_mut()?.rfind(predicate)
    }

    /* unstable
    fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
        where
            Self: Sized,
            F: FnMut(B, Self::Item) -> R,
            R: core::ops::Try<Output = B>, {

    }
    */

    fn rfold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
    where
        Self: Sized,
        F: FnMut(Acc, Self::Item) -> Acc,
    {
        // This function is consuming, so we don't need to worry much about disposing of
        // anything, just returning when needed :)
        let mut had_any_elements = false;
        if let Some(b) = self.b {
            acc = b.rfold(acc, |acc, item| {
                had_any_elements = true;
                f(acc, item)
            });
        }
        if had_any_elements {
            // Consuming function, no need to deinit `a`
            return acc;
        }

        if let Some(a) = self.a {
            acc = a.rfold(acc, f);
        }
        acc
    }
}

/// Execute the given function on the option, clearing the option if the output from the function
/// was [`None`]. Also allows providing a callback to run if the output of `f` was `Some`.
///
/// This is pretty much identical to the function inside the standard library, except with added
/// callback.
#[inline]
fn and_then_or_clear<T, U>(
    resetting_input: &mut Option<T>,
    with_some_input: impl FnOnce(&mut T) -> Option<U>,
    on_some: impl FnOnce(),
) -> Option<U> {
    let output = with_some_input(resetting_input.as_mut()?);
    if output.is_none() {
        *resetting_input = None;
    } else {
        on_some();
    };
    output
}

/// Trait for extending [`Iterator`]s with methods to create [`EmptyFallbackChain`] iterators.
pub trait IteratorExt: Iterator {
    /// Takes two iterators and creates a new iterator that runs through the second only if the
    /// first produces no output. Can take anything implementing [`IntoIterator`] as a second
    /// argument.
    ///
    /// `empty_fallback_chain()` will return a new iterator which will iterate over the first iterator. If
    /// it produces any values, then the second iterator is dropped. However, if it doesn't, then
    /// the second iterator is iterated over instead.
    ///
    /// In other words, it links two iterators in a chain, but only if the first is empty.
    ///
    /// # Examples
    ///
    /// Basic usage:
    /// ```
    /// use empty_fallback_chain::prelude::*;
    /// let higher_priority = [1, 2, 3];
    /// let lower_priority = [4, 5, 6];
    ///
    /// let iter = higher_priority.into_iter().empty_fallback_chain(lower_priority.into_iter());
    /// assert_eq!(iter.collect::<Vec<_>>(), vec![1, 2, 3]);
    /// ```
    ///
    /// The major feature of [`EmptyFallbackChain`] is that if the first iterator produces
    /// no values, then the second iterator will be used instead.
    /// ```
    /// use empty_fallback_chain::IteratorExt as _;
    ///
    /// let higher_priority = [1, 3, 5];
    /// let lower_priority = [10, 11, 78];
    ///
    /// /// Filter for even numbers - no data in the higher priority iterator matches this,
    /// /// so when the filtered version is used as the first half of an `EmptyFallbackChain`,
    /// /// the "fallback" iterator is what's used.
    /// fn even(v: &u32) -> bool {
    ///     v % 2 == 0
    /// }
    ///
    /// let iter = higher_priority.into_iter().filter(even)
    ///     .empty_fallback_chain(lower_priority.into_iter());
    /// assert_eq!(iter.collect::<Vec<_>>(), vec![10, 11, 78]);
    /// ```
    ///
    /// If the higher priority iterator produces *any* values, then the lower priority iterator is
    /// never used. For example, with a filter that doesn't remove all of the higher-priority
    /// information:
    /// ```
    /// use empty_fallback_chain::prelude::*;
    ///
    /// let higher_priority = [1, 3, 5];
    /// let lower_priority = [10, 11, 78];
    ///
    /// fn incomplete_higher_filter(v: &u32) -> bool {
    ///    *v != 3
    /// }
    ///
    /// let iter = higher_priority.into_iter().filter(incomplete_higher_filter)
    ///     .empty_fallback_chain(lower_priority.into_iter());
    /// assert_eq!(iter.collect::<Vec<_>>(), vec![1, 5]);
    /// ```
    ///
    /// This can be used to create incredibly powerful, lazily evaluated, fallback systems.
    /// If you use multiple [`EmptyFallbackChain`] in sequence, you can create a sort of
    /// "iterator priority" construction.
    /// ```
    /// use empty_fallback_chain::prelude::*;
    ///
    /// #[derive(Debug, Clone, PartialEq, Eq, Hash)]
    /// pub struct Contact {
    ///     pub name: &'static str,
    ///     pub email: &'static str,
    ///     pub pronouns: &'static str
    /// }
    ///
    ///# impl Contact {
    ///#    /// Just example - You'd probably use owned, interned, or referenced strings
    ///#    /// here.
    ///#    pub const fn new(
    ///#        name: &'static str,
    ///#        email: &'static str,
    ///#        pronouns: &'static str
    ///#    ) -> Self {
    ///#        Self { name, email, pronouns }
    ///#    }
    ///# }
    /// // Example conditions
    /// fn is_tuesday() -> bool { true }
    /// fn is_wednesday() -> bool { false }
    /// fn is_weekend() -> bool { false }
    ///
    /// use Contact as C;
    /// use core::iter as iter;
    ///
    /// const bob: C = C::new("Bob Angie", "the-eponymous-bob@example.com", "he/him");
    /// const tracey: C = C::new("Tracy Mill", "tracy-mill@corpo.example.com", "she/her");
    /// const alan: C = C::new("Alan", "alanspace@example.com", "he/him");
    /// const matriss: C = C::new("Matriss Karisle", "matriss@example.com", "they/them");
    /// const charlie: C = C::new("Charlie Stone", "charlie-charlie@example.com", "she/her");
    /// const harri: C = C::new("Harri", "harri-up@example.com", "they/she");
    /// const mel: C = C::new("Mel", "mel@corpo.example.com", "she/her");
    /// const troy: C = C::new("Troy", "helenofcity@example.com", "he/him");
    ///
    /// // Define the contact lists as functions producing iterators, in order of preference.
    /// // In reality, you'd use some better means of determining availability, but the principle
    /// // of using fallbacks is sound, and can be used for many scenarios
    ///
    /// fn emergency_contacts() -> impl Iterator<Item = Contact> {
    ///     iter::empty()
    ///         .chain(iter::once(troy).filter(|_| !is_tuesday() && !is_weekend()))
    ///         .chain(iter::once(charlie).filter(|_| !is_weekend()))
    ///         .chain([bob, matriss, harri].into_iter().filter(|_| !is_tuesday() &&
    ///             !is_wednesday()))
    /// }
    ///
    /// fn distant_family_contacts() -> impl Iterator<Item = Contact> {
    ///     // fill in here
    ///#    iter::empty()
    ///#        .chain(iter::once(tracey).filter(|_| !is_weekend() && !is_tuesday()))
    /// }
    ///
    /// fn corpo_contacts() -> impl Iterator<Item = Contact> {
    ///     // fill in here
    ///#    iter::empty()
    ///#        .chain(iter::once(mel).filter(|_| !is_weekend()))
    ///#        .chain(iter::once(tracey))
    /// }
    ///
    /// fn friendly_evening_contacts() -> impl Iterator<Item = Contact> {
    ///     // fill in here
    ///#    iter::empty()
    ///#        .chain([matriss, harri, mel].into_iter()
    ///#            .filter(|_| !is_weekend() && !is_tuesday()))
    ///#        .chain([alan, troy, charlie].into_iter()
    ///#            .filter(|_| !is_weekend() && !is_wednesday()))
    /// }
    ///
    /// // Then, build contact scenarios, using `empty_fallback_chain`
    /// // If there are no contacts available for emergency situations, then
    /// // this will iterate for contacts who can just be messaged for a "friendly evening".
    /// // If that fails, then it will iterate over all distant family contacts.
    /// fn i_have_an_emergency() -> impl Iterator<Item = Contact> {
    ///     emergency_contacts()
    ///         .empty_fallback_chain(friendly_evening_contacts())
    ///         .empty_fallback_chain(distant_family_contacts())
    ///
    /// }
    ///
    /// fn i_want_a_friendly_time() -> impl Iterator<Item = Contact> {
    ///     friendly_evening_contacts()
    ///         .empty_fallback_chain(distant_family_contacts())
    ///         .empty_fallback_chain(corpo_contacts())
    /// }
    ///
    /// fn i_am_having_an_existential_crisis() -> impl Iterator<Item = Contact> {
    ///     // fill in here
    ///#    distant_family_contacts()
    ///#        .empty_fallback_chain(friendly_evening_contacts())
    /// }
    /// ```
    #[inline]
    fn empty_fallback_chain<U>(self, other: U) -> EmptyFallbackChain<Self, U::IntoIter>
    where
        Self: Sized,
        U: IntoIterator<Item = Self::Item>,
    {
        EmptyFallbackChain::new(self, other.into_iter())
    }
}

impl<T: Iterator + ?Sized> IteratorExt for T {}

pub mod prelude {
    pub use super::IteratorExt as _;
}

#[cfg(test)]
mod tests {
    use core::iter;

    fn none_iter<T: Iterator>() -> iter::Flatten<core::option::IntoIter<T>> {
        None.into_iter().flatten()
    }

    fn some_iter<T: Iterator>(t: T) -> iter::Flatten<core::option::IntoIter<T>> {
        Some(t).into_iter().flatten()
    }

    use super::*;
    /// Make the "first" iterator to compose [`make_conditional_iter`]
    fn make_first_iter() -> impl DoubleEndedIterator<Item = u32> + Clone {
        [0].into_iter()
    }

    /// Make the "second" iterator to compose [`make_conditional_iter`]
    fn make_second_iter() -> impl DoubleEndedIterator<Item = u32> + Clone {
        [10, 11].into_iter()
    }

    /// Make the "third" iterator to compose [`make_conditional_iter`]
    fn make_third_iter() -> impl DoubleEndedIterator<Item = u32> + Clone {
        [20, 21, 22, 23, 24, 25].into_iter()
    }

    /// All values that might be emitted from an iterator made by [`make_conditional_iter`]
    /// Not intended for direct comparison, but just for getting all the values.
    fn make_all_values_iter() -> impl Iterator<Item = u32> + Clone {
        make_first_iter()
            .chain(make_second_iter())
            .chain(make_third_iter())
    }

    /// Get a value not present in any possible [`make_conditional_iter`] combination.
    fn non_present_value() -> u32 {
        make_all_values_iter().max().unwrap() + 1
    }

    /// Make an "equivalent" known-good iterator for the given 3-boolean configuration,
    /// only for the forward-order though
    fn make_ideal_equivalent_iter_for(
        include_values: [bool; 3],
    ) -> impl Iterator<Item = u32> + Clone {
        match include_values {
            [true, _, _] => some_iter(make_first_iter())
                .chain(none_iter())
                .chain(none_iter()),
            [false, true, _] => none_iter()
                .chain(some_iter(make_second_iter()))
                .chain(none_iter()),
            [false, false, true] => none_iter()
                .chain(none_iter())
                .chain(some_iter(make_third_iter())),
            [false, false, false] => none_iter().chain(none_iter()).chain(none_iter()),
        }
    }

    /// Make an "equivalent" known-good (for the reverse direction) [`DoubleEndedIterator`] for the
    /// given 3-boolean configuration, only for the reverse order functions though.
    fn make_ideal_equivalent_reverse_iter_for(
        include_values: [bool; 3],
    ) -> impl DoubleEndedIterator<Item = u32> + Clone {
        match include_values {
            [_, _, true] => none_iter()
                .chain(none_iter())
                .chain(some_iter(make_third_iter())),
            [_, true, false] => none_iter()
                .chain(some_iter(make_second_iter()))
                .chain(none_iter()),
            [true, false, false] => some_iter(make_first_iter())
                .chain(none_iter())
                .chain(none_iter()),
            [false, false, false] => none_iter().chain(none_iter()).chain(none_iter()),
        }
    }

    /// Create a 3 element iterator chained with [EmptyFallbackChain] and where each given
    /// subiterator either has values or does not depending on the booleans.
    ///
    /// The values in question are, if present, for the iterators in order:
    /// * `[0]`,
    /// * `[10, 11]`
    /// * `[20, 21, 22, 23, 24, 25]`
    fn make_conditional_iter(
        include_values: [bool; 3],
    ) -> impl DoubleEndedIterator<Item = u32> + Clone {
        let first_iter = make_first_iter().filter(move |_| include_values[0]);
        let second_iter = make_second_iter().filter(move |_| include_values[1]);
        let third_iter = make_third_iter().filter(move |_| include_values[2]);
        first_iter
            .empty_fallback_chain(second_iter)
            .empty_fallback_chain(third_iter)
    }

    /// Compare the functionality of a pair of iterators that should be "equivalent"
    /// This means basic things, but also the more advanced iterator methods.
    ///
    /// Note that this will not compare [`DoubleEndedIterator`] methods - because the equivalent
    /// "known good" iterator might be different for the opposite direction.
    fn compare_iters(
        known_good: impl Iterator<Item = u32> + Clone,
        to_test: impl Iterator<Item = u32> + Clone,
    ) {
        // Test contents
        assert_eq!(
            known_good.clone().collect::<Vec<_>>(),
            to_test.clone().collect::<Vec<_>>()
        );

        assert_eq!(known_good.clone().count(), to_test.clone().count());

        assert_eq!(
            known_good.clone().fold(3, |a, b| a + b),
            to_test.clone().fold(3, |a, b| a + b)
        );

        for possible_value in make_all_values_iter().chain(iter::once(non_present_value())) {
            assert_eq!(
                known_good.clone().find(|v| *v == possible_value),
                to_test.clone().find(|v| *v == possible_value)
            )
        }

        assert_eq!(known_good.last(), to_test.last());
    }

    /// Compare specifically the reverse-based functionality of a pair of double ended iterators.
    /// This is separated out from [`compare_iters`] because of divergent inverse behaviour which
    /// means there is a different "ideal" model for the inverse mode.
    fn compare_inverse_iters(
        known_good: impl DoubleEndedIterator<Item = u32> + Clone,
        to_test: impl DoubleEndedIterator<Item = u32> + Clone,
    ) {
        // Test inverse contents.
        assert_eq!(
            known_good.clone().rev().collect::<Vec<_>>(),
            to_test.clone().rev().collect::<Vec<_>>()
        );

        // Test reverse-find
        for possible_value in make_all_values_iter().chain(iter::once(non_present_value())) {
            assert_eq!(
                known_good.clone().rfind(|v| *v == possible_value),
                to_test.clone().rfind(|v| *v == possible_value)
            );
        }

        // Test rfold
        assert_eq!(
            known_good.rfold(3, |acc, v| acc + v),
            to_test.rfold(3, |acc, v| acc + v)
        );
    }

    /// Run tests for the [`make_conditional_iter`] generated by the given boolean triplet, against
    /// the "ideal" iterator that it should be equivalent to if [`EmptyFallbackChain`] works
    /// correctly.
    fn compare_boolean_made_iters(include_values: [bool; 3]) {
        compare_iters(
            make_ideal_equivalent_iter_for(include_values),
            make_conditional_iter(include_values),
        );
    }

    /// Run tests for the [`make_conditional_iter`] generated by the given boolean triplet, against
    /// the "ideal" double ended iterator that it's inverse "double-ended" methods should be
    /// equivalent to if [`EmptyFallbackChain`] works correctly.
    fn compare_boolean_made_inverse_iters(include_values: [bool; 3]) {
        compare_inverse_iters(
            make_ideal_equivalent_reverse_iter_for(include_values),
            make_conditional_iter(include_values),
        )
    }

    #[test]
    fn chained_priority_basics() {
        for i0 in [false, true] {
            for i1 in [false, true] {
                for i2 in [false, true] {
                    compare_boolean_made_iters([i0, i1, i2]);
                    compare_boolean_made_inverse_iters([i0, i1, i2]);
                }
            }
        }
    }
}

// This Source Code Form is subject to the terms of the Mozilla Public
//  License, v. 2.0. If a copy of the MPL was not distributed with this
//  file, You can obtain one at http://mozilla.org/MPL/2.0/.