cons_cell 0.1.0

Swiss knife for heterogenous containers
Documentation
use crate::cons::types::hcons::HNilSeed;
use crate::cons::{Cons, ConsCell, Extend, Nil};

pub trait MapFn<Input> {
    type Output;
    fn map(input: Input) -> Self::Output;
}

pub trait MapFnsInto<F, NilSeed>: ConsCell {
    type Output: ConsCell;
    fn map_recursive_into(self) -> Self::Output;
}

impl<F, C: Cons, NilSeed> MapFnsInto<F, NilSeed> for C
where
    F: MapFn<C::Head>,
    C::Tail: MapFnsInto<F, NilSeed>,
    <C::Tail as MapFnsInto<F, NilSeed>>::Output: Extend<<F as MapFn<C::Head>>::Output>,
{
    type Output = <<C::Tail as MapFnsInto<F, NilSeed>>::Output as Extend<
        <F as MapFn<C::Head>>::Output,
    >>::Extension;

    #[inline(always)]
    fn map_recursive_into(self) -> Self::Output {
        let (head, tail) = self.split();
        Self::Output::new(
            <F as MapFn<C::Head>>::map(head),
            <C::Tail as MapFnsInto<F, NilSeed>>::map_recursive_into(tail),
        )
    }
}

impl<F, InNilSeed, OutNilSeed> MapFnsInto<F, OutNilSeed> for Nil<InNilSeed> {
    type Output = Nil<OutNilSeed>;

    #[inline(always)]
    fn map_recursive_into(self) -> Self::Output {
        Self::Output::new()
    }
}

pub trait MapFns<F>: ConsCell {
    type Output: ConsCell;

    fn map_recursive(self) -> Self::Output;
}

impl<F, C: MapFnsInto<F, HNilSeed>> MapFns<F> for C {
    type Output = <C as MapFnsInto<F, HNilSeed>>::Output;

    #[inline(always)]
    fn map_recursive(self) -> Self::Output {
        self.map_recursive_into()
    }
}

pub trait Map: ConsCell {
    #[inline(always)]
    fn map<F>(self) -> <Self as MapFns<F>>::Output
    where
        Self: MapFns<F>,
    {
        <Self as MapFns<F>>::map_recursive(self)
    }

    #[inline(always)]
    fn map_into<F, NilSeed>(self) -> <Self as MapFnsInto<F, NilSeed>>::Output
    where
        Self: MapFnsInto<F, NilSeed>,
    {
        <Self as MapFnsInto<F, NilSeed>>::map_recursive_into(self)
    }
}

impl<C: ConsCell> Map for C {}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::cons::types::hcons::HNil;

    struct ToUsize {}

    impl<T: Into<usize>> MapFn<T> for ToUsize {
        type Output = usize;

        fn map(input: T) -> usize {
            input.into()
        }
    }

    struct DoubleUsize {}

    impl MapFn<usize> for DoubleUsize {
        type Output = usize;

        fn map(input: usize) -> Self::Output {
            input * 2
        }
    }

    #[test]
    fn it_works() {
        let x = HNil::new().extend(1u8).extend(2u16);

        let y = x.map::<ToUsize>().map::<DoubleUsize>();

        assert_eq!(y.head(), &4usize);
        assert_eq!(y.tail().head(), &2usize);
    }

    fn map_to_usize<C: Cons>(c: C) -> <C as MapFns<ToUsize>>::Output
    where
        C: MapFns<ToUsize>,
    {
        c.map::<ToUsize>()
    }

    #[test]
    fn it_works_generic() {
        let x = HNil::new().extend(1u8).extend(2u16);

        let y = map_to_usize(x);

        assert_eq!(y.head(), &2usize);
        assert_eq!(y.tail().head(), &1usize);
    }

    fn map_to_usize_then_double<C: Cons>(
        c: C,
    ) -> <<C as MapFns<ToUsize>>::Output as MapFns<DoubleUsize>>::Output
    where
        C: MapFns<ToUsize>,
        <C as MapFns<ToUsize>>::Output: MapFns<DoubleUsize>,
    {
        c.map::<ToUsize>().map::<DoubleUsize>()
    }

    #[test]
    fn it_works_generic_chained() {
        let x = HNil::new().extend(1u8).extend(2u16);

        let y = map_to_usize_then_double(x);

        assert_eq!(y.head(), &4usize);
        assert_eq!(y.tail().head(), &2usize);
    }

    //TODO : Test map_into.
}