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
/*
* 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 https://mozilla.org/MPL/2.0/.
*/
//! Breadth-first exhaustive `zip` for repeatable iterators.
//! Behavior matches the following pseudocode specification:
//! - Initialize a counter `i` at zero.
//! - When propmted, pull the first element from each iterator.
//! - If any iterator is empty, return `None`.
//! - When prompted again, advance only the last iterator.
//! - Continue to do so until the last iterator terminates or reaches its `i`th element.
//! - When it does so, reset it and pull the next element from the second-to-last iterator.
//! - Repeat this process until we exhaust the first iterator.
//! - When you've done that, increase `i` and repeat.
//! - Once `i` exceeds the longest iterator's length, we're done: return `None`.
// #![cfg_attr(not(test), no_std)] // FIXME
#![deny(warnings)]
#![warn(
clippy::all,
clippy::missing_docs_in_private_items,
clippy::nursery,
clippy::pedantic,
clippy::restriction,
clippy::cargo,
missing_docs,
rustdoc::all
)]
#![allow(
clippy::blanket_clippy_restriction_lints,
clippy::cognitive_complexity,
clippy::expect_used,
clippy::implicit_return,
clippy::inline_always,
clippy::needless_borrowed_reference,
clippy::panic,
clippy::question_mark_used,
clippy::separated_literal_suffix,
clippy::string_add,
clippy::unwrap_used
)]
mod impure;
#[cfg(feature = "alloc")]
mod pure;
#[cfg(test)]
mod test_impure;
#[cfg(all(test, feature = "alloc"))]
mod test_pure;
/// End of a recursive implementation of a breadth-first exhaustive `zip`.
#[derive(Clone)]
pub struct BaseCase(bool);
/// Index and value.
#[derive(Clone, Debug)]
pub struct Indexed<Type> {
/// Index.
index: usize,
/// Value.
value: Type,
}
/// Construct an `Indexed` from a tuple.
#[allow(clippy::missing_const_for_fn)]
#[inline(always)]
#[must_use]
pub fn indexed<Type>((index, value): (usize, Type)) -> Indexed<Type> {
Indexed { index, value }
}
/// Flatten a nested tuple like `(A, (B, (C, ())))` to a flat one like `(A, B, C)`
pub trait Flatten {
/// Flat tuple, e.g. `(A, B, C)`, not `(A, (B, (C, ())))`.
type Flattened;
/// Flatten e.g. `(A, (B, (C, ())))` into `(A, B, C)`.
#[must_use]
fn flatten(self) -> Self::Flattened;
}
impl Flatten for () {
type Flattened = Self;
#[inline(always)]
#[must_use]
fn flatten(self) -> Self::Flattened {}
}
breadth_first_zip_macros::implement_flatten!();
/// Sealed traits.
mod sealed {
/// Either `BaseCase` or `BreadthFirst<Whatever, ...>` eventually ending in `BaseCase` on the right-hand side.
pub trait BreadthFirst {}
impl BreadthFirst for crate::BaseCase {}
#[cfg(feature = "alloc")]
impl<Head: Iterator, Tail: crate::BreadthFirst> BreadthFirst
for crate::pure::BreadthFirstZipped<Head, Tail>
{
}
impl<Head: Iterator + Clone, Tail: crate::BreadthFirst> BreadthFirst
for crate::impure::BreadthFirstZipped<Head, Tail>
{
}
}
/// Helper trait returning a nested list that will be turned into a flat list for a huge but finite range of tuple sizes.
pub trait BreadthFirst: sealed::BreadthFirst {
/// Depth of recursion.
const DEPTH: usize;
/// Output of `advance` if successful.
type Advance: Flatten;
/// Fallibly choose the next output.
#[must_use]
fn advance(&mut self, index_sum: usize) -> Option<Self::Advance>;
/// Rewind the iterator back to its starting point
fn rewind(&mut self);
}
impl BreadthFirst for BaseCase {
const DEPTH: usize = 0;
type Advance = ();
#[inline(always)]
#[must_use]
fn advance(&mut self, index_sum: usize) -> Option<Self::Advance> {
(index_sum == 0 && self.0).then(|| {
self.0 = false;
})
}
#[inline(always)]
fn rewind(&mut self) {
self.0 = true;
}
}