#[cfg(any(test, feature = "testing"))]
use std::fmt::Debug;
use std::sync::atomic::AtomicBool;
use check_stopped::CheckStopped;
use on_final_count::OnFinalCount;
use crate::common::iterator_ext::stoppable_iter::StoppableIter;
pub(super) mod on_final_count;
mod check_stopped;
mod fallible;
pub mod ordering_iterator;
pub mod stoppable_iter;
pub use fallible::{FallibleIteratorExt, TransposeResultIter};
pub trait IteratorExt: Iterator {
fn check_stop_every<F>(self, every: usize, f: F) -> CheckStopped<Self, F>
where
F: FnMut() -> bool,
Self: Sized,
{
CheckStopped::new(self, every, f)
}
#[inline]
fn stop_if<'a>(self, is_stopped: &'a AtomicBool) -> StoppableIter<'a, Self>
where
Self: Sized,
{
StoppableIter::new(self, is_stopped)
}
fn on_final_count<F>(self, f: F) -> OnFinalCount<Self, F>
where
F: FnMut(usize),
Self: Sized,
{
OnFinalCount::new(self, f)
}
fn black_box(self)
where
Self: Sized,
{
self.for_each(|p| {
std::hint::black_box(p);
});
}
fn try_any<F, E>(&mut self, mut f: F) -> Result<bool, E>
where
F: FnMut(Self::Item) -> Result<bool, E>,
Self: Sized,
{
self.find_map(|item| match f(item) {
Ok(true) => Some(Ok(true)),
Ok(false) => None,
Err(e) => Some(Err(e)),
})
.unwrap_or(Ok(false))
}
}
impl<I: Iterator> IteratorExt for I {}
#[cfg(any(test, feature = "testing"))]
pub fn check_iterator_fold<I: Iterator, F: Fn() -> I>(mk_iter: F)
where
I::Item: PartialEq + Debug,
{
const EXTRA_COUNT: usize = 3;
let mut reference_values = Vec::new();
let mut iter = mk_iter();
#[expect(
clippy::while_let_on_iterator,
reason = "Reference implementation: call bare-bones `next()` explicitly"
)]
while let Some(value) = iter.next() {
reference_values.push(value);
}
for _ in 0..EXTRA_COUNT {
assert!(
iter.next().is_none(),
"Iterator returns values after it's exhausted",
);
}
drop(iter);
let mut values_for_fold = Vec::new();
for split_at in 0..reference_values.len() + EXTRA_COUNT {
let mut iter = mk_iter();
values_for_fold.clear();
for _ in 0..split_at.min(reference_values.len()) {
values_for_fold.push(iter.next().expect("not enough values"));
}
for _ in 0..split_at.saturating_sub(reference_values.len()) {
assert!(iter.next().is_none());
}
let acc = iter.fold(values_for_fold.len(), |acc, value| {
assert_eq!(acc, values_for_fold.len());
values_for_fold.push(value);
acc + 1
});
assert_eq!(reference_values, values_for_fold);
assert_eq!(acc, values_for_fold.len());
}
}
#[cfg(any(test, feature = "testing"))]
pub fn check_exact_size_iterator_len<I: ExactSizeIterator>(mut iter: I) {
for expected_len in (0..iter.len()).rev() {
iter.next();
assert_eq!(iter.len(), expected_len);
}
assert!(iter.next().is_none());
assert_eq!(iter.len(), 0);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn try_any() {
let is_even = |n: i32| match n {
n if n < 0 => Err("negative"),
n => Ok(n % 2 == 0),
};
assert_eq!([1, 3, 4, 5].into_iter().try_any(is_even), Ok(true));
assert_eq!([1, 3, 5, 7].into_iter().try_any(is_even), Ok(false));
assert_eq!(std::iter::empty().try_any(is_even), Ok(false));
assert_eq!([1, 3, -1, 4].into_iter().try_any(is_even), Err("negative"));
let mut iter = [1, 2, 3, -1].into_iter();
assert_eq!(iter.try_any(is_even), Ok(true));
assert_eq!(iter.next(), Some(3));
}
}