harper 0.0.1

A library to implement SSGs.
Documentation
#[derive(Debug, Copy, Clone)]
pub struct Nil;

#[derive(Debug, Clone)]
pub struct Cons<H, T: ?Sized> {
    pub head: H,
    pub tail: T,
}

pub trait HList {
    const LEN: usize;

    fn insert<T>(self, value: T) -> Cons<T, Self>;
    fn len(&self) -> usize { Self::LEN }
    fn is_empty(&self) -> bool { Self::LEN == 0 }
}

impl HList for Nil {
    const LEN: usize = 0;

    fn insert<V>(self, value: V) -> Cons<V, Self> {
        Cons { head: value, tail: self }
    }
}

impl<H, T: HList> HList for Cons<H, T> {
    const LEN: usize = T::LEN + 1;

    fn insert<V>(self, value: V) -> Cons<V, Self> {
        Cons { head: value, tail: self }
    }
}

impl<H, T> Cons<H, T> {
    pub fn pop(self) -> (H, T) {
        (self.head, self.tail)
    }
}

pub trait Func<T> {
    type Output;

    fn call(&self, value: T) -> Self::Output;
}

pub trait Mappable<F> {
    type Output;

    fn map(self, f: F) -> Self::Output;
}

impl<F> Mappable<F> for Nil {
    type Output = Nil;

    fn map(self, _: F) -> Self::Output {
        Nil
    }
}

impl<H, F: Func<H>, T: Mappable<F>> Mappable<F> for Cons<H, T> {
    type Output = Cons<F::Output, <T as Mappable<F>>::Output>;

    fn map(self, f: F) -> Self::Output {
        Cons {
            head: F::call(&f, self.head),
            tail: self.tail.map(f)
        }
    }
}

pub trait FuncMut<T> {
    type Output;

    fn call_mut(&mut self, value: T) -> Self::Output;
}

pub trait MappableMut<F> {
    type Output;

    fn map_mut(self, f: F) -> Self::Output;
}

impl<F> MappableMut<F> for Nil {
    type Output = Nil;

    fn map_mut(self, _: F) -> Self::Output {
        Nil
    }
}

impl<H, F, T> MappableMut<F> for Cons<H, T>
    where F: FuncMut<H>,
          T: MappableMut<F>
{
    type Output = Cons<F::Output, <T as MappableMut<F>>::Output>;

    fn map_mut(self, mut f: F) -> Self::Output {
        Cons {
            head: F::call_mut(&mut f, self.head),
            tail: self.tail.map_mut(f)
        }
    }
}

pub trait Foldable<A, F> {
    type Output;

    fn fold(self, acc: A, f: F) -> Self::Output;
}

impl<A, F> Foldable<A, F> for Nil {
    type Output = A;

    fn fold(self, acc: A, _: F) -> Self::Output {
        acc
    }
}

impl<A, F, H, T> Foldable<A, F> for Cons<H, T>
    where F: Func<(H, A)>,
          T: Foldable<F::Output, F>
{
    type Output = T::Output;

    fn fold(self, acc: A, f: F) -> Self::Output {
        let output = F::call(&f, (self.head, acc));
        self.tail.fold(output, f)
    }
}

pub trait ToMut<'a> {
    type Output;

    fn to_mut(&'a mut self) -> Self::Output;
}

impl<'a> ToMut<'a> for Nil {
    type Output = Nil;

    fn to_mut(&'a mut self) -> Self::Output {
        Nil
    }
}

impl<'a, H: 'a, T: ToMut<'a>> ToMut<'a> for Cons<H, T> {
    type Output = Cons<&'a mut H, <T as ToMut<'a>>::Output>;

    fn to_mut(&'a mut self) -> Self::Output {
        Cons {
            head: &mut self.head,
            tail: self.tail.to_mut()
        }
    }
}

pub trait ToRef<'a> {
    type Output;

    fn to_ref(&'a self) -> Self::Output;
}

impl<'a> ToRef<'a> for Nil {
    type Output = Nil;

    fn to_ref(&'a self) -> Self::Output {
        Nil
    }
}

impl<'a, H: 'a, T: ToRef<'a>> ToRef<'a> for Cons<H, T> {
    type Output = Cons<&'a H, <T as ToRef<'a>>::Output>;

    fn to_ref(&'a self) -> Self::Output {
        Cons {
            head: &self.head,
            tail: self.tail.to_ref()
        }
    }
}

mod macros {
    #[doc(hidden)]
    #[macro_export]
    macro_rules! hlist {
        () => ( $crate::util::hlist::Nil );

        ($h:expr $(, $t:expr)* $(,)?) => {
            $crate::util::hlist::Cons {
                head: $h,
                tail: hlist!($($t),*)
            }
        };
    }

    #[doc(hidden)]
    #[macro_export]
    macro_rules! HList {
        () => ($crate::util::hlist::Nil);

        (.. $T:ty) => ($T);

        ($H:ty) => {
            $crate::util::hlist::Cons<$H, HList![]>
        };

        ($H:ty , $($T:tt)*) => {
            $crate::util::hlist::Cons<$H, HList!($($T)*)>
        };
    }

    #[doc(hidden)]
    #[macro_export]
    macro_rules! lfold {
        ([] $hlist:expr, $acc:expr, |$i:pat_param, $j:pat_param| $f:expr) => ({
            $acc
        });

        ([$dot:tt $($dots:tt)*] $hlist:expr, $acc:expr, |$i:pat_param, $j:pat_param| $f:expr) => ({
            let list = $hlist;
            let ($i, _rest) = list.pop();
            let $j = $acc;
            let acc = $f;
            $crate::util::hlist::lfold!([$($dots)*] _rest, acc, |$i, $j| $f)
        })
    }

    #[doc(hidden)]
    #[macro_export]
    macro_rules! rfold {
        ([] $hlist:expr, $acc:expr, |$i:pat_param, $j:pat_param| $f:expr) => ({
            $acc
        });

        ([$dot:tt $($dots:tt)*] $hlist:expr, $acc:expr, |$i:pat_param, $j:pat_param| $f:expr) => ({
            let list = $hlist;
            let ($i, _rest) = list.pop();
            let $j = $crate::util::hlist::rfold!([$($dots)*] _rest, $acc, |$i, $j| $f);
            $f
        })
    }

    #[doc(hidden)]
    #[macro_export]
    macro_rules! for_each_mut {
        ([$($dots:tt)*] $hlist:expr, |$i:pat_param| $f:expr) => (
            $crate::util::hlist::rfold!([$($dots)*] $hlist, (), |$i, _| $f)
        )
    }

    pub use {HList, hlist, rfold, lfold, for_each_mut};
}

#[doc(inline)]
pub use macros::*;

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

    #[test]
    fn test_generic_map() {
        struct DebugPrint;

        impl<T: std::fmt::Debug> Func<T> for DebugPrint {
            type Output = ();

            fn call(&self, value: T) -> Self::Output {
                println!("{:?}", value);
            }
        }

        #[derive(Debug)]
        struct Foo;

        let list: HList![usize, &str, Foo] = hlist![1, "hello", Foo];
        list.map(DebugPrint);
    }

    #[test]
    fn test_generic_fold() {
        struct DebugString;

        impl<T: std::fmt::Debug> Func<(T, String)> for DebugString {
            type Output = String;

            fn call(&self, (input, acc): (T, String)) -> Self::Output {
                format!("{}, {:?}", acc, input)
            }
        }

        #[derive(Debug)]
        struct Foo;

        let list: HList![usize, &str, Foo] = hlist![1, "hello", Foo];
        list.fold("".to_string(), DebugString);
    }
}