typsy 0.1.0

Heterogenous containers
Documentation
use crate::hlist::{Cons, HList, Nil, Shuffle};

pub use core::marker::PhantomData;

pub use macros::Transform;

mod deep_transform;
pub use deep_transform::{DeepTransform, DeepTransformFrom};

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Named<T, Name: 'static>(pub T, PhantomData<Name>);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Unnamed<T>(pub T);

impl<T, Name: 'static> Named<T, Name> {
    pub const fn new(value: T) -> Self { Self(value, PhantomData) }
}

#[macro_export]
macro_rules! field {
    ($field_name:ident) => {
        $crate::macros::name! {$crate $field_name}
    };
}

#[macro_export]
macro_rules! Anon {
    (
        $($($field_name:ident: $field_ty:ty),+ $(, @$rest:ty)? $(,)?)?
    ) => {
        $crate::HList!($($($crate::anon::Named::<$field_ty, $crate::field!($field_name)>),* $(, @$rest)?)?)
    };
    (
        $($($field_ty:ty),+ $(, @$rest:ty)? $(,)?)?
    ) => {
        $crate::HList!($($($crate::anon::Unnamed::<$field_ty>),* $(, @$rest)?)?)
    };
}

#[macro_export]
macro_rules! anon {
    (
        $($($field_name:ident = $field_val:expr),+ $(,)?)?
    ) => {
        $crate::hlist!($($($crate::anon::Named::<_, $crate::field!($field_name)>::new($field_val)),*)?)
    };
    (
        $($($field_val:expr),+ $(,)?)?
    ) => {
        $crate::hlist!($($($crate::anon::Unnamed($field_val)),*)?)
    };
}

pub trait AnonType: HList {}
impl AnonType for Nil {}
impl<T, R: AnonType, Name> AnonType for Cons<Named<T, Name>, R> {}
impl<T, R: AnonType> AnonType for Cons<Unnamed<T>, R> {}

impl<T> Access for T {}
pub trait Access {
    fn field<N, T, Name: 'static>(&self) -> &T
    where
        Self: crate::hlist::Access<Named<T, Name>, N>,
    {
        &self.get().0
    }

    fn field_mut<N, T, Name: 'static>(&mut self) -> &mut T
    where
        Self: crate::hlist::Access<Named<T, Name>, N>,
    {
        &mut self.get_mut().0
    }

    fn take_field<N, T, Name>(self) -> (T, Self::Remainder)
    where
        Self: crate::hlist::Access<Named<T, Name>, N>,
    {
        let (field, rest) = self.take();
        (field.0, rest)
    }
}

pub trait RemoveField<N, I> {
    type Value;
    type Remainder;

    fn remove_field(self) -> (Self::Value, Self::Remainder);
}

impl<T, Name, R> RemoveField<Name, crate::peano::Zero> for Cons<Named<T, Name>, R> {
    type Value = T;
    type Remainder = R;

    fn remove_field(self) -> (Self::Value, Self::Remainder) { (self.value.0, self.rest) }
}

impl<T, Name, R: RemoveField<Name, N>, N> RemoveField<Name, crate::peano::Succ<N>> for Cons<T, R> {
    type Value = R::Value;
    type Remainder = Cons<T, R::Remainder>;

    fn remove_field(self) -> (Self::Value, Self::Remainder) {
        let (value, rest) = self.rest.remove_field();
        (value, Cons {
            value: self.value,
            rest,
        })
    }
}

pub trait Transform: Sized {
    type Canon: AnonType;

    fn from_canon(anon: Self::Canon) -> Self;

    fn into_canon(self) -> Self::Canon;

    fn transform<O: Transform, N>(self) -> O
    where
        Self::Canon: Shuffle<O::Canon, N>,
    {
        O::from_canon(self.into_canon().shuffle())
    }

    fn deep_transform<O: Transform, N>(self) -> O
    where
        Self::Canon: DeepTransform<O::Canon, N>,
    {
        O::from_canon(self.into_canon().deep_transform())
    }
}

impl Transform for Nil {
    type Canon = Self;

    fn from_canon(anon: Self::Canon) -> Self { anon }

    fn into_canon(self) -> Self::Canon { self }
}

impl<T, R> Transform for Cons<T, R>
where
    Self: AnonType,
{
    type Canon = Self;

    fn from_canon(anon: Self::Canon) -> Self { anon }

    fn into_canon(self) -> Self::Canon { self }
}

impl<T: AnonType> IntoNamed for T {}
pub trait IntoNamed: Sized + AnonType {
    fn into_named<T, N>(self) -> T
    where
        Self: Shuffle<T::Canon, N>,
        T: Transform,
    {
        T::from_canon(self.shuffle())
    }
}

#[doc(hidden)]
#[macro_export]
macro_rules! character {
    ($($c:ident,)*) => {
        $crate::HList!($($crate::anon::character::$c),*)
    };
}

#[allow(non_camel_case_types)]
pub mod character {
    macro_rules! build_character {
        ($($c:ident)*) => {$(
            #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
            pub enum $c {}
        )*};
    }

    build_character! {
        a b c d e f g h i j k l m n o p q r s t u v w x y z
        A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
        _0 _1 _2 _3 _4 _5 _6 _7 _8 _9 __
    }
}