use core::{marker::Destruct, ops::Try};
use array_trait::{AsArray, length};
use currying::Curry;
use crate::{Bulk, DoubleEndedBulk, IntoBulk, IntoContained, StaticBulk};
#[must_use = "bulks are lazy and do nothing unless consumed"]
#[derive(Clone, Debug)]
pub struct FlatMap<I, F>
where
I: Bulk,
F: FnMut<(I::Item,), Output: IntoBulk<IntoBulk: StaticBulk>>
{
bulk: I,
map: F
}
impl<I, U, F> FlatMap<I, F>
where
I: Bulk,
F: FnMut(I::Item) -> U,
U: IntoBulk<IntoBulk: StaticBulk>
{
pub(crate) const fn new(bulk: I, map: F) -> Self
{
Self { bulk, map }
}
const fn chunk() -> usize
{
<<<U as IntoBulk>::IntoBulk as StaticBulk>::Array<U::Item> as AsArray>::LENGTH
}
}
impl<I, U, F> IntoIterator for FlatMap<I, F>
where
I: Bulk,
F: FnMut(I::Item) -> U,
U: IntoBulk<IntoBulk: StaticBulk>
{
type IntoIter = <<core::iter::FlatMap<I::IntoIter, U, F> as IntoContained>::IntoContained as IntoIterator>::IntoIter;
type Item = U::Item;
fn into_iter(self) -> Self::IntoIter
{
let Self { bulk, map } = self;
unsafe { bulk.into_iter().flat_map(map).into_contained().into_iter() }
}
}
const impl<I, U, F> Bulk for FlatMap<I, F>
where
I: [const] Bulk<Item: [const] Destruct>,
F: [const] FnMut(I::Item) -> U + [const] Destruct,
U: [const] IntoBulk<IntoBulk: StaticBulk + [const] Bulk>
{
type MaxLength = length::Mul<I::MaxLength, <U::IntoBulk as StaticBulk>::Array<()>>;
type MinLength = length::Mul<I::MinLength, <U::IntoBulk as StaticBulk>::Array<()>>;
fn len(&self) -> usize
{
let Self { bulk, map: _ } = self;
bulk.len() * Self::chunk()
}
fn is_empty(&self) -> bool
{
let Self { bulk, map: _ } = self;
Self::chunk() == 0 || bulk.is_empty()
}
fn first(self) -> Option<Self::Item>
where
Self::Item: [const] Destruct,
Self: Sized
{
const fn into_first<F, T, U>(f: F, x: T) -> Option<U::Item>
where
F: [const] FnOnce(T) -> U,
U: [const] IntoBulk<Item: [const] Destruct>
{
f(x).into_bulk().first()
}
let Self { bulk, mut map } = self;
bulk.first().and_then(into_first.curry(&mut map))
}
fn for_each<FF>(self, f: FF)
where
Self: Sized,
FF: [const] FnMut(Self::Item) + [const] Destruct
{
struct Closure<F, FF>
{
map: F,
f: FF
}
const impl<F, FF, U, T> FnOnce<(T,)> for Closure<F, FF>
where
F: [const] FnOnce(T) -> U,
U: [const] IntoBulk<IntoBulk: StaticBulk + [const] Bulk>,
FF: [const] FnMut(U::Item) + [const] Destruct
{
type Output = ();
extern "rust-call" fn call_once(self, (x,): (T,)) -> Self::Output
{
(self.map)(x).into_bulk().for_each(self.f)
}
}
const impl<F, FF, U, T> FnMut<(T,)> for Closure<F, FF>
where
F: [const] FnMut(T) -> U,
U: [const] IntoBulk<IntoBulk: StaticBulk + [const] Bulk>,
FF: [const] FnMut(U::Item) + [const] Destruct
{
extern "rust-call" fn call_mut(&mut self, (x,): (T,)) -> Self::Output
{
(self.map)(x).into_bulk().for_each(&mut self.f)
}
}
let Self { bulk, map } = self;
bulk.for_each(Closure { map, f })
}
fn try_for_each<FF, R>(self, f: FF) -> R
where
Self: Sized,
Self::Item: [const] Destruct,
FF: [const] FnMut(Self::Item) -> R + [const] Destruct,
R: [const] Try<Output = ()>
{
struct Closure<F, FF>
{
map: F,
f: FF
}
const impl<F, FF, U, T, R> FnOnce<(T,)> for Closure<F, FF>
where
F: [const] FnOnce(T) -> U,
U: [const] IntoBulk<IntoBulk: StaticBulk + [const] Bulk, Item: [const] Destruct>,
FF: [const] FnMut(U::Item) -> R + [const] Destruct,
R: [const] Try<Output = ()>
{
type Output = R;
extern "rust-call" fn call_once(self, (x,): (T,)) -> Self::Output
{
(self.map)(x).into_bulk().try_for_each(self.f)
}
}
const impl<F, FF, U, T, R> FnMut<(T,)> for Closure<F, FF>
where
F: [const] FnMut(T) -> U,
U: [const] IntoBulk<IntoBulk: StaticBulk + [const] Bulk, Item: [const] Destruct>,
FF: [const] FnMut(U::Item) -> R + [const] Destruct,
R: [const] Try<Output = ()>
{
extern "rust-call" fn call_mut(&mut self, (x,): (T,)) -> Self::Output
{
(self.map)(x).into_bulk().try_for_each(&mut self.f)
}
}
let Self { bulk, map } = self;
bulk.try_for_each(Closure { map, f })
}
}
const impl<I, U, F> DoubleEndedBulk for FlatMap<I, F>
where
I: [const] DoubleEndedBulk<Item: [const] Destruct>,
F: [const] FnMut(I::Item) -> U + [const] Destruct,
U: [const] IntoBulk<IntoBulk: StaticBulk + [const] DoubleEndedBulk>,
Self::IntoIter: DoubleEndedIterator
{
fn rev_for_each<FF>(self, f: FF)
where
Self: Sized,
FF: [const] FnMut(Self::Item) + [const] Destruct
{
struct Closure<F, FF>
{
map: F,
f: FF
}
const impl<F, FF, U, T> FnOnce<(T,)> for Closure<F, FF>
where
F: [const] FnOnce(T) -> U,
U: [const] IntoBulk<IntoBulk: StaticBulk + [const] DoubleEndedBulk>,
FF: [const] FnMut(U::Item) + [const] Destruct
{
type Output = ();
extern "rust-call" fn call_once(self, (x,): (T,)) -> Self::Output
{
(self.map)(x).into_bulk().rev_for_each(self.f)
}
}
const impl<F, FF, U, T> FnMut<(T,)> for Closure<F, FF>
where
F: [const] FnMut(T) -> U,
U: [const] IntoBulk<IntoBulk: StaticBulk + [const] DoubleEndedBulk>,
FF: [const] FnMut(U::Item) + [const] Destruct
{
extern "rust-call" fn call_mut(&mut self, (x,): (T,)) -> Self::Output
{
(self.map)(x).into_bulk().rev_for_each(&mut self.f)
}
}
let Self { bulk, map } = self;
bulk.rev_for_each(Closure { map, f })
}
fn try_rev_for_each<FF, R>(self, f: FF) -> R
where
Self: Sized,
Self::Item: [const] Destruct,
FF: [const] FnMut(Self::Item) -> R + [const] Destruct,
R: [const] Try<Output = ()>
{
struct Closure<F, FF>
{
map: F,
f: FF
}
const impl<F, FF, U, T, R> FnOnce<(T,)> for Closure<F, FF>
where
F: [const] FnOnce(T) -> U,
U: [const] IntoBulk<IntoBulk: StaticBulk + [const] DoubleEndedBulk, Item: [const] Destruct>,
FF: [const] FnMut(U::Item) -> R + [const] Destruct,
R: [const] Try<Output = ()>
{
type Output = R;
extern "rust-call" fn call_once(self, (x,): (T,)) -> Self::Output
{
(self.map)(x).into_bulk().try_rev_for_each(self.f)
}
}
const impl<F, FF, U, T, R> FnMut<(T,)> for Closure<F, FF>
where
F: [const] FnMut(T) -> U,
U: [const] IntoBulk<IntoBulk: StaticBulk + [const] DoubleEndedBulk, Item: [const] Destruct>,
FF: [const] FnMut(U::Item) -> R + [const] Destruct,
R: [const] Try<Output = ()>
{
extern "rust-call" fn call_mut(&mut self, (x,): (T,)) -> Self::Output
{
(self.map)(x).into_bulk().try_rev_for_each(&mut self.f)
}
}
let Self { bulk, map } = self;
bulk.try_rev_for_each(Closure { map, f })
}
}
#[cfg(test)]
mod test
{
use crate::*;
#[test]
fn it_works()
{
let a = [1, 2, 3];
let b = a.into_bulk().flat_map(|x| [x, -x]).collect::<[_; _], _>();
println!("{b:?}")
}
}