bulks 0.6.6

Amazing bulks! They are like iterators, but in bulk, and therefore support collection into arrays.
Documentation
use array_trait::length::{self, LengthValue};

use crate::{Bulk, DoubleEndedBulk, IntoBulk, SplitBulk};

pub const fn merge<Lhs, Rhs, F>(lhs: impl ~const IntoBulk<IntoBulk = Lhs>, rhs: impl ~const IntoBulk<IntoBulk = Rhs>, f: F) -> Merge<Lhs, Rhs, F>
where
    Lhs: Bulk<Item: Into<<F as FnOnce<(Lhs::Item, Rhs::Item)>>::Output>>,
    Rhs: Bulk<Item: Into<<F as FnOnce<(Lhs::Item, Rhs::Item)>>::Output>>,
    F: FnMut<(Lhs::Item, Rhs::Item)>
{
    Merge::new(lhs.into_bulk(), rhs.into_bulk(), f)
}

pub struct Merge<Lhs, Rhs, F>
where
    Lhs: Bulk<Item: Into<<F as FnOnce<(Lhs::Item, Rhs::Item)>>::Output>>,
    Rhs: Bulk<Item: Into<<F as FnOnce<(Lhs::Item, Rhs::Item)>>::Output>>,
    F: FnMut<(Lhs::Item, Rhs::Item)>
{
    lhs: Lhs,
    rhs: Rhs,
    f: F
}

impl<Lhs, Rhs, F, O> Merge<Lhs, Rhs, F>
where
    Lhs: Bulk<Item: Into<O>>,
    Rhs: Bulk<Item: Into<O>>,
    F: FnMut(Lhs::Item, Rhs::Item) -> O
{
    pub(crate) const fn new(lhs: Lhs, rhs: Rhs, f: F) -> Self
    {
        Self {
            lhs,
            rhs,
            f
        }
    }
}

const fn merge_once<F, Lhs, Rhs, O>(f: &mut F, lhs: Option<Lhs>, rhs: Option<Rhs>) -> Option<O>
where
    F: ~const FnMut(Lhs, Rhs) -> O,
    Lhs: ~const Into<O>,
    Rhs: ~const Into<O>
{
    match (lhs, rhs)
    {
        (Some(lhs), Some(rhs)) => Some(f(lhs, rhs)),
        (None, Some(x)) => Some(x.into()),
        (Some(x), None) => Some(x.into()),
        (None, None) => None
    }
}

mod private
{
    use crate::{adapters::merge::merge_once};

    pub struct IntoIter<Lhs, Rhs, F, O>
    where
        Lhs: Iterator<Item: Into<O>>,
        Rhs: Iterator<Item: Into<O>>,
        F: FnMut(Lhs::Item, Rhs::Item) -> O
    {
        pub lhs: Lhs,
        pub rhs: Rhs,
        pub f: F
    }

    impl<Rhs, Lhs, F, O> Iterator for IntoIter<Lhs, Rhs, F, O>
    where
        Lhs: Iterator<Item: Into<O>>,
        Rhs: Iterator<Item: Into<O>>,
        F: FnMut(Lhs::Item, Rhs::Item) -> O
    {
        type Item = O;
        
        fn next(&mut self) -> Option<Self::Item>
        {
            let Self { lhs, rhs, f } = self;
            merge_once(f, lhs.next(), rhs.next())
        }
        fn size_hint(&self) -> (usize, Option<usize>)
        {
            let Self { lhs, rhs, f: _ } = self;
            let (lhs_min, lhs_max) = lhs.size_hint();
            let (rhs_min, rhs_max) = rhs.size_hint();
            (
                lhs_min.max(rhs_min),
                merge_once(&mut |lhs_max: usize, rhs_max: usize| lhs_max.max(rhs_max), lhs_max, rhs_max)
            )
        }
    }
    impl<Rhs, Lhs, F, O> ExactSizeIterator for IntoIter<Lhs, Rhs, F, O>
    where
        Lhs: ExactSizeIterator<Item: Into<O>>,
        Rhs: ExactSizeIterator<Item: Into<O>>,
        F: FnMut(Lhs::Item, Rhs::Item) -> O
    {
        fn is_empty(&self) -> bool
        {
            let Self { lhs, rhs, f: _ } = self;
            lhs.is_empty() && rhs.is_empty()
        }
        fn len(&self) -> usize
        {
            let Self { lhs, rhs, f: _ } = self;
            lhs.len().max(rhs.len())
        }
    }
    impl<Rhs, Lhs, F, O> DoubleEndedIterator for IntoIter<Lhs, Rhs, F, O>
    where
        Lhs: DoubleEndedIterator<Item: Into<O>> + ExactSizeIterator,
        Rhs: DoubleEndedIterator<Item: Into<O>> + ExactSizeIterator,
        F: FnMut(Lhs::Item, Rhs::Item) -> O
    {
        fn next_back(&mut self) -> Option<Self::Item>
        {
            let Self { lhs, rhs, f } = self;
            let lhs_len = lhs.len();
            let rhs_len = rhs.len();
            if lhs_len > rhs_len
            {
                return lhs.next_back().map(Into::into)
            }
            if rhs_len > lhs_len
            {
                return rhs.next_back().map(Into::into)
            }
            merge_once(f, lhs.next_back(), rhs.next_back())
        }
    }
}

impl<Lhs, Rhs, F, O> IntoIterator for Merge<Lhs, Rhs, F>
where
    Lhs: Bulk<Item: Into<O>>,
    Rhs: Bulk<Item: Into<O>>,
    F: FnMut(Lhs::Item, Rhs::Item) -> O
{
    type Item = O;
    type IntoIter = private::IntoIter<Lhs::IntoIter, Rhs::IntoIter, F, O>;

    fn into_iter(self) -> Self::IntoIter
    {
        let Self { lhs, rhs, f } = self;
        private::IntoIter {
            lhs: lhs.into_iter(),
            rhs: rhs.into_iter(),
            f
        }
    }
}
impl<Lhs, Rhs, F, O> Bulk for Merge<Lhs, Rhs, F>
where
    Lhs: Bulk<Item: Into<O>>,
    Rhs: Bulk<Item: Into<O>>,
    F: FnMut(Lhs::Item, Rhs::Item) -> O
{
    type Length = length::Max<Lhs::Length, Rhs::Length>;
    type MaxLength = length::Max<Lhs::MaxLength, Rhs::MaxLength>;
    type MinLength = length::Max<Lhs::MinLength, Rhs::MinLength>;

    fn len(&self) -> usize
    {
        let Self { lhs, rhs, f: _ } = self;
        Ord::max(lhs.len(), rhs.len())
    }
    fn is_empty(&self) -> bool
    {
        let Self { lhs, rhs, f: _ } = self;
        lhs.is_empty() && rhs.is_empty()
    }

    fn first(self) -> Option<Self::Item>
    where
        Self: Sized
    {
        let Self { lhs, rhs, mut f } = self;
        merge_once(&mut f, lhs.first(), rhs.first())
    }

    fn for_each<FF>(self, f: FF)
    where
        Self: Sized,
        FF: FnMut(Self::Item)
    {
        self.into_iter().for_each(f)
    }
    fn try_for_each<FF, R>(self, f: FF) -> R
    where
        Self: Sized,
        FF: FnMut(Self::Item) -> R,
        R: core::ops::Try<Output = ()>
    {
        self.into_iter().try_for_each(f)
    }
}
impl<Lhs, Rhs, F, O> DoubleEndedBulk for Merge<Lhs, Rhs, F>
where
    Lhs: DoubleEndedBulk<Item: Into<O>>,
    Rhs: DoubleEndedBulk<Item: Into<O>>,
    F: FnMut(Lhs::Item, Rhs::Item) -> O
{
    fn rev_for_each<FF>(self, f: FF)
    where
        Self: Sized,
        FF: FnMut(Self::Item)
    {
        self.into_iter().rev().for_each(f)
    }
    fn try_rev_for_each<FF, R>(self, f: FF) -> R
    where
        Self: Sized,
        FF: FnMut(Self::Item) -> R,
        R: core::ops::Try<Output = ()>
    {
        self.into_iter().rev().try_for_each(f)
    }
}
impl<Lhs, Rhs, F, O, L> const SplitBulk<L> for Merge<Lhs, Rhs, F>
where
    Lhs: ~const SplitBulk<L, Item: Into<O>>,
    Rhs: ~const SplitBulk<L, Item: Into<O>>,
    Self: ~const Bulk,
    Merge<Lhs::Left, Rhs::Left, F>: ~const Bulk<Item = Self::Item>,
    Merge<Lhs::Right, Rhs::Right, F>: ~const Bulk<Item = Self::Item>,
    F: FnMut(Lhs::Item, Rhs::Item) -> O + Copy,
    L: LengthValue
{
    type Left = Merge<Lhs::Left, Rhs::Left, F>;
    type Right = Merge<Lhs::Right, Rhs::Right, F>;

    fn split_at(Self { lhs, rhs, f }: Self, n: L) -> (Self::Left, Self::Right)
    where
        Self: Sized
    {
        let (a_left, a_right) = lhs.split_at(n);
        let (b_left, b_right) = rhs.split_at(n);
        
        (
            Merge::new(a_left, b_left, f),
            Merge::new(a_right, b_right, f),
        )
    }
}

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

    #[test]
    fn it_works()
    {
        let a = [1, 3, 5];
        let b = [2, 4, 6, 7];
        let bulk = a.into_bulk()
            .merge(b, |a, b| a + b);
        let c = bulk.collect::<[_; _], _>();
        
        println!("{c:?}");
        assert_eq!(c, [3, 7, 11, 7])
    }
}