bulks 0.11.3

Amazing bulks! They are like iterators, but in bulk, and therefore support collection into arrays.
Documentation
use core::{marker::Destruct, ops::Try};

use array_trait::length;

use crate::{Bulk, DoubleEndedBulk, util::ArrayBuffer};

/// A bulk over the mapped windows of another bulk.
///
/// This `struct` is created by the [`Bulk::map_windows`]. See its
/// documentation for more information.
#[must_use = "bulks are lazy and do nothing unless consumed"]
pub struct MapWindows<I, F, const N: usize>
where
    I: Bulk,
    F: for<'a> FnMut<(&'a [I::Item; N],)>
{
    bulk: I,
    f: F
}

impl<I: Bulk, F, U, const N: usize> MapWindows<I, F, N>
where
    I: Bulk,
    F: FnMut(&[I::Item; N]) -> U
{
    pub(crate) const fn new(bulk: I, f: F) -> Self
    {
        assert!(N != 0, "array in `Bulk::map_windows` must contain more than 0 elements");

        // Only ZST arrays' length can be so large.
        if core::mem::size_of::<I::Item>() != 0
        {
            assert!(N.checked_mul(2).is_some(), "array size of `Iterator::map_windows` is too large");
        }

        Self { bulk, f }
    }
}

const impl<I: Bulk, F, U, const N: usize> IntoIterator for MapWindows<I, F, N>
where
    I: Bulk + [const] IntoIterator<IntoIter: [const] Iterator>,
    F: FnMut(&[I::Item; N]) -> U
{
    type IntoIter = core::iter::MapWindows<I::IntoIter, F, N>;
    type Item = U;

    fn into_iter(self) -> Self::IntoIter
    {
        let Self { bulk, f } = self;
        bulk.into_iter().map_windows(f)
    }
}

const impl<I: Bulk, F, U, const N: usize> Bulk for MapWindows<I, F, N>
where
    I: [const] Bulk<Item: [const] Destruct>,
    F: [const] FnMut(&[I::Item; N]) -> U + [const] Destruct
{
    type MaxLength = length::Windowed<I::MaxLength, [(); N]>;
    type MinLength = length::Windowed<I::MinLength, [(); N]>;

    fn len(&self) -> usize
    {
        let Self { bulk, f: _ } = self;
        bulk.len().saturating_sub(N - 1)
    }

    fn is_empty(&self) -> bool
    {
        let Self { bulk, f: _ } = self;
        bulk.len() > N - 1
    }

    fn for_each<FF>(self, f: FF)
    where
        Self: Sized,
        FF: [const] FnMut(Self::Item) + [const] Destruct
    {
        let Self { bulk, f: map } = self;
        bulk.for_each(Closure::<_, _, _, _, _, false> {
            map,
            f,
            buffer: ArrayBuffer::new()
        });
    }

    fn try_for_each<FF, R>(self, f: FF) -> R
    where
        Self: Sized,
        FF: [const] FnMut(Self::Item) -> R + [const] Destruct,
        R: [const] core::ops::Try<Output = ()>
    {
        let Self { bulk, f: map } = self;
        bulk.try_for_each(TryClosure::<_, _, _, _, _, _, false> {
            map,
            f,
            buffer: ArrayBuffer::new()
        })
    }
}
const impl<I: Bulk, F, U, const N: usize> DoubleEndedBulk for MapWindows<I, F, N>
where
    I: [const] DoubleEndedBulk<Item: [const] Destruct>,
    F: [const] FnMut(&[I::Item; N]) -> U + [const] Destruct,
    Self::IntoIter: DoubleEndedIterator
{
    fn rev_for_each<FF>(self, f: FF)
    where
        Self: Sized,
        FF: [const] FnMut(Self::Item) + [const] Destruct
    {
        let Self { bulk, f: map } = self;
        bulk.rev_for_each(Closure::<_, _, _, _, _, true> {
            map,
            f,
            buffer: ArrayBuffer::new()
        });
    }

    fn try_rev_for_each<FF, R>(self, f: FF) -> R
    where
        Self: Sized,
        FF: [const] FnMut(Self::Item) -> R + [const] Destruct,
        R: [const] core::ops::Try<Output = ()>
    {
        let Self { bulk, f: map } = self;
        bulk.try_rev_for_each(TryClosure::<_, _, _, _, _, _, true> {
            map,
            f,
            buffer: ArrayBuffer::new()
        })
    }
}

struct Closure<F, FF, T, U, const N: usize, const REV: bool>
where
    F: FnMut(&[T; N]) -> U,
    FF: FnMut(U)
{
    map: F,
    f: FF,
    buffer: ArrayBuffer<T, N, REV>
}
const impl<F, FF, T, U, const N: usize, const REV: bool> FnOnce<(T,)> for Closure<F, FF, T, U, N, REV>
where
    T: [const] Destruct,
    F: [const] FnMut(&[T; N]) -> U + [const] Destruct,
    FF: [const] FnMut(U) + [const] Destruct
{
    type Output = ();

    extern "rust-call" fn call_once(mut self, args: (T,)) -> Self::Output
    {
        self.call_mut(args)
    }
}
const impl<F, FF, T, U, const N: usize, const REV: bool> FnMut<(T,)> for Closure<F, FF, T, U, N, REV>
where
    T: [const] Destruct,
    F: [const] FnMut(&[T; N]) -> U,
    FF: [const] FnMut(U)
{
    extern "rust-call" fn call_mut(&mut self, (x,): (T,)) -> Self::Output
    {
        let Self { map, f, buffer } = self;
        buffer.push_out(x);
        if let Some(window) = buffer.as_array()
        {
            f(map(window))
        }
    }
}

struct TryClosure<F, FF, T, U, R, const N: usize, const REV: bool>
where
    F: FnMut(&[T; N]) -> U,
    FF: FnMut(U) -> R
{
    map: F,
    f: FF,
    buffer: ArrayBuffer<T, N, REV>
}
const impl<F, FF, T, U, R, const N: usize, const REV: bool> FnOnce<(T,)> for TryClosure<F, FF, T, U, R, N, REV>
where
    T: [const] Destruct,
    F: [const] FnMut(&[T; N]) -> U + [const] Destruct,
    FF: [const] FnMut(U) -> R + [const] Destruct,
    R: [const] Try<Output = ()>
{
    type Output = R;

    extern "rust-call" fn call_once(mut self, args: (T,)) -> Self::Output
    {
        self.call_mut(args)
    }
}
const impl<F, FF, T, U, R, const N: usize, const REV: bool> FnMut<(T,)> for TryClosure<F, FF, T, U, R, N, REV>
where
    T: [const] Destruct,
    F: [const] FnMut(&[T; N]) -> U,
    FF: [const] FnMut(U) -> R,
    R: [const] Try<Output = ()>
{
    extern "rust-call" fn call_mut(&mut self, (x,): (T,)) -> Self::Output
    {
        let Self { map, f, buffer } = self;
        buffer.push_out(x);
        if let Some(window) = buffer.as_array()
        {
            f(map(window))?
        }
        R::from_output(())
    }
}

#[cfg(test)]
mod test
{
    use crate::*;

    #[test]
    fn it_works()
    {
        let mut i = 0;
        let a = crate::repeat_n_with(
            || {
                i += 1;
                i
            },
            [(); 20]
        )
        .map_windows(|&[n, m]| n + m)
        .collect::<[_; _], _>();

        println!("{a:?}")
    }
}