#![doc = include_str!("../README.md")]
#![cfg_attr(not(test), no_std)]
#[inline]
pub fn empty_fallback_chain<A: IntoIterator, B: IntoIterator<Item = A::Item>>(
a: A,
b: B,
) -> EmptyFallbackChain<A::IntoIter, B::IntoIter> {
EmptyFallbackChain::new(a.into_iter(), b.into_iter())
}
#[inline]
pub fn maybe_empty_fallback_chain<A: IntoIterator, B: IntoIterator<Item = A::Item>>(
a: A,
b: Option<B>,
) -> EmptyFallbackChain<A::IntoIter, B::IntoIter> {
EmptyFallbackChain::new_with_pre_empty(Some(a.into_iter()), b.map(IntoIterator::into_iter))
}
#[inline]
pub fn pre_empty_fallback_chain<A: IntoIterator, B: IntoIterator<Item = A::Item>>(
a: Option<A>,
b: Option<B>,
) -> EmptyFallbackChain<A::IntoIter, B::IntoIter> {
EmptyFallbackChain::new_with_pre_empty(
a.map(IntoIterator::into_iter),
b.map(IntoIterator::into_iter),
)
}
#[derive(Debug, Clone)]
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub struct EmptyFallbackChain<A, B> {
a: Option<A>,
b: Option<B>,
}
impl<A, B> EmptyFallbackChain<A, B> {
#[inline]
pub const fn new(a: A, b: B) -> EmptyFallbackChain<A, B> {
Self {
a: Some(a),
b: Some(b),
}
}
#[inline]
pub const fn new_with_pre_empty(a: Option<A>, b: Option<B>) -> EmptyFallbackChain<A, B> {
Self { a, b }
}
}
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,
};
if a_count != 0 {
return a_count;
}
match self.b {
Some(b) => b.count(),
None => 0,
}
}
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 {
return acc;
}
if let Some(b) = self.b {
acc = b.fold(acc, f);
}
acc
}
#[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;
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;
}
self.b.as_mut()?.find(predicate)
}
#[inline]
fn last(self) -> Option<Self::Item>
where
Self: Sized,
{
let a_last = self.a.and_then(Iterator::last);
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)) => {
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> {
and_then_or_clear(&mut self.b, DoubleEndedIterator::next_back, || {
self.a = None;
})
.or_else(|| self.a.as_mut()?.next_back())
}
#[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;
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)
}
fn rfold<Acc, F>(self, mut acc: Acc, mut f: F) -> Acc
where
Self: Sized,
F: FnMut(Acc, Self::Item) -> Acc,
{
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 {
return acc;
}
if let Some(a) = self.a {
acc = a.rfold(acc, f);
}
acc
}
}
#[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
}
pub trait IteratorExt: Iterator {
#[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())
}
#[inline]
fn maybe_empty_fallback_chain<U>(
self,
other: Option<U>,
) -> EmptyFallbackChain<Self, U::IntoIter>
where
Self: Sized,
U: IntoIterator<Item = Self::Item>,
{
EmptyFallbackChain::new_with_pre_empty(Some(self), other.map(IntoIterator::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::*;
fn make_first_iter() -> impl DoubleEndedIterator<Item = u32> + Clone {
[0].into_iter()
}
fn make_second_iter() -> impl DoubleEndedIterator<Item = u32> + Clone {
[10, 11].into_iter()
}
fn make_third_iter() -> impl DoubleEndedIterator<Item = u32> + Clone {
[20, 21, 22, 23, 24, 25].into_iter()
}
fn make_all_values_iter() -> impl Iterator<Item = u32> + Clone {
make_first_iter()
.chain(make_second_iter())
.chain(make_third_iter())
}
fn non_present_value() -> u32 {
make_all_values_iter().max().unwrap() + 1
}
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()),
}
}
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()),
}
}
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)
}
fn compare_iters(
known_good: impl Iterator<Item = u32> + Clone,
to_test: impl Iterator<Item = u32> + Clone,
) {
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());
}
fn compare_inverse_iters(
known_good: impl DoubleEndedIterator<Item = u32> + Clone,
to_test: impl DoubleEndedIterator<Item = u32> + Clone,
) {
assert_eq!(
known_good.clone().rev().collect::<Vec<_>>(),
to_test.clone().rev().collect::<Vec<_>>()
);
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)
);
}
assert_eq!(
known_good.rfold(3, |acc, v| acc + v),
to_test.rfold(3, |acc, v| acc + v)
);
}
fn compare_boolean_made_iters(include_values: [bool; 3]) {
compare_iters(
make_ideal_equivalent_iter_for(include_values),
make_conditional_iter(include_values),
);
}
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]);
}
}
}
}
}