use core::marker::Destruct;
use array_trait::length::{self, Length, LengthValue};
use crate::{Bulk, BulkLength, DoubleEndedBulk, IntoBulk, IntoContained, SplitBulk};
#[derive(Clone, Debug)]
#[must_use = "bulks are lazy and do nothing unless consumed"]
pub struct Chain<A, B>
where
A: Bulk,
B: Bulk<Item = A::Item>
{
a: A,
b: B
}
impl<A, B> Chain<A, B>
where
A: Bulk,
B: Bulk<Item = A::Item>
{
pub(crate) const fn new(a: A, b: B) -> Self
{
Self { a, b }
}
}
pub const fn chain<A, B>(a: A, b: B) -> Chain<A::IntoBulk, B::IntoBulk>
where
A: [const] IntoBulk,
B: [const] IntoBulk<Item = A::Item>
{
Chain::new(a.into_bulk(), b.into_bulk())
}
const impl<A, B, T> IntoIterator for Chain<A, B>
where
A: Bulk<Item = T> + [const] IntoIterator<IntoIter: [const] Iterator>,
B: Bulk<Item = T> + [const] IntoIterator<IntoIter: [const] Iterator>,
core::iter::Chain<A::IntoIter, B::IntoIter>: [const] IntoContained<IntoIter: [const] Iterator, IntoContained: [const] IntoIterator>
{
type IntoIter = <<core::iter::Chain<A::IntoIter, B::IntoIter> as IntoContained>::IntoContained as IntoIterator>::IntoIter;
type Item = <<core::iter::Chain<A::IntoIter, B::IntoIter> as IntoContained>::IntoContained as IntoIterator>::Item;
fn into_iter(self) -> Self::IntoIter
{
let Self { a, b } = self;
unsafe { a.into_iter().chain(b).into_contained().into_iter() }
}
}
const impl<A, B, T> Bulk for Chain<A, B>
where
A: [const] Bulk<Item = T> + [const] Destruct,
B: [const] Bulk<Item = T> + [const] Destruct
{
type MaxLength = length::Add<A::MaxLength, B::MaxLength>;
type MinLength = length::Add<A::MinLength, B::MinLength>;
fn len(&self) -> usize
{
let Self { a, b } = self;
a.len() + b.len()
}
fn is_empty(&self) -> bool
{
let Self { a, b } = self;
a.is_empty() && b.is_empty()
}
fn first(self) -> Option<Self::Item>
where
Self::Item: [const] Destruct,
Self: Sized
{
let Self { a, b } = self;
match a.first()
{
Some(first) => Some(first),
None => b.first()
}
}
fn for_each<F>(self, mut f: F)
where
Self: Sized,
F: [const] FnMut(Self::Item) + [const] Destruct
{
let Self { a, b } = self;
a.for_each(&mut f);
b.for_each(f);
}
fn try_for_each<F, R>(self, mut f: F) -> R
where
Self: Sized,
T: [const] Destruct,
F: [const] FnMut(Self::Item) -> R + [const] Destruct,
R: [const] core::ops::Try<Output = ()>
{
let Self { a, b } = self;
a.try_for_each(&mut f)?;
b.try_for_each(f)
}
}
const impl<A, B, T> DoubleEndedBulk for Chain<A, B>
where
A: [const] DoubleEndedBulk<Item = T> + [const] Destruct,
B: [const] DoubleEndedBulk<Item = T> + [const] Destruct,
Self::IntoIter: DoubleEndedIterator
{
fn rev_for_each<F>(self, mut f: F)
where
Self: Sized,
F: [const] FnMut(Self::Item) + [const] Destruct
{
let Self { a, b } = self;
b.rev_for_each(&mut f);
a.rev_for_each(f);
}
fn try_rev_for_each<F, R>(self, mut f: F) -> R
where
Self: Sized,
T: [const] Destruct,
F: [const] FnMut(Self::Item) -> R + [const] Destruct,
R: [const] core::ops::Try<Output = ()>
{
let Self { a, b } = self;
b.try_rev_for_each(&mut f)?;
a.try_rev_for_each(f)
}
}
const impl<A, B, T, D, L, R> SplitBulk<L> for Chain<A, B>
where
A: [const] SplitBulk<L, Item = T, Left: [const] Bulk, Right: [const] Bulk> + [const] Bulk + [const] Destruct,
B: [const] SplitBulk<R, Item = T, Left: [const] Bulk, Right: [const] Bulk> + [const] Destruct,
BulkLength<A>: Length<Value = D>,
L: LengthValue<SaturatingSub<D> = R>,
R: LengthValue,
D: LengthValue
{
type Left = Chain<A::Left, B::Left>;
type Right = Chain<A::Right, B::Right>;
fn split_at(Self { a, b }: Self, n: L) -> (Self::Left, Self::Right)
where
Self: Sized
{
let m = length::value::saturating_sub(n, a.length());
let (a_left, a_right) = a.split_at(n);
let (b_left, b_right) = b.split_at(m);
(a_left.chain(b_left), a_right.chain(b_right))
}
}
#[cfg(test)]
mod test
{
use crate::*;
#[test]
fn it_works()
{
let (a, b) = const {
let a = [1, 2, 3];
let b = [4, 5, 6];
let (a, b) = a.into_bulk().chain(b).split_at([(); 4]);
(a.collect_array(), b.collect_array())
};
println!("{a:?} {b:?}")
}
}