typsy 0.1.0

Heterogenous containers
Documentation
use crate::peano;

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CoNil {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum CoCons<T, R> {
    Value(T),
    Rest(R),
}

#[macro_export]
macro_rules! CoProd {
    () => { $crate::coprod::CoNil };
    (, @$last:ty) => { $last };
    ($first:ty $(, $rest:ty)* $(, @$last:ty)* $(,)?) => { $crate::coprod::CoCons<$first, $crate::CoProd!($($rest),* $(, @$last)?)> };
}

pub trait CoProd: Sized + crate::Seal {}
impl crate::Seal for CoNil {}
impl CoProd for CoNil {}

impl<T, R: crate::Seal> crate::Seal for CoCons<T, R> {}
impl<T, R: CoProd> CoProd for CoCons<T, R> {}

pub trait Uninhabitted {
    fn uninhabitted(self) -> !;
}

impl Uninhabitted for core::convert::Infallible {
    fn uninhabitted(self) -> ! { match self {} }
}

impl Uninhabitted for CoNil {
    fn uninhabitted(self) -> ! { match self {} }
}

impl<T: Uninhabitted, R: Uninhabitted> Uninhabitted for CoCons<T, R> {
    fn uninhabitted(self) -> ! {
        match self {
            CoCons::Value(value) => value.uninhabitted(),
            CoCons::Rest(rest) => rest.uninhabitted(),
        }
    }
}

pub trait Access<T, N>: CoProd {
    type Remainder: CoProd;

    fn take(self) -> Result<T, Self::Remainder>;

    fn get(&self) -> Option<&T>;

    fn get_mut(&mut self) -> Option<&mut T>;

    fn put(value: T) -> Self;
}

pub trait IntoSuperset<T: CoProd, N>: CoProd {
    fn into_superset(self) -> T;
}

pub trait IntoSubset<T: CoProd, N>: CoProd {
    type Remainder: CoProd;

    fn into_subset(self) -> Result<T, Self::Remainder>;
}

impl<T, R: CoProd> Access<T, peano::Zero> for CoCons<T, R> {
    type Remainder = R;

    fn take(self) -> Result<T, Self::Remainder> {
        match self {
            Self::Value(value) => Ok(value),
            Self::Rest(rest) => Err(rest),
        }
    }

    fn get(&self) -> Option<&T> {
        match self {
            Self::Value(value) => Some(value),
            Self::Rest(_) => None,
        }
    }

    fn get_mut(&mut self) -> Option<&mut T> {
        match self {
            Self::Value(value) => Some(value),
            Self::Rest(_) => None,
        }
    }

    fn put(value: T) -> Self { Self::Value(value) }
}

impl<T, U, R: Access<T, N>, N> Access<T, peano::Succ<N>> for CoCons<U, R> {
    type Remainder = CoCons<U, R::Remainder>;

    fn take(self) -> Result<T, Self::Remainder> {
        match self {
            Self::Value(value) => Err(CoCons::Value(value)),
            Self::Rest(rest) => rest.take().map_err(CoCons::Rest),
        }
    }

    fn get(&self) -> Option<&T> {
        match self {
            Self::Value(_) => None,
            Self::Rest(rest) => rest.get(),
        }
    }

    fn get_mut(&mut self) -> Option<&mut T> {
        match self {
            Self::Value(_) => None,
            Self::Rest(rest) => rest.get_mut(),
        }
    }

    fn put(value: T) -> Self { CoCons::Rest(R::put(value)) }
}

impl<T: CoProd> IntoSuperset<T, crate::TyList!()> for CoNil {
    fn into_superset(self) -> T { match self {} }
}

impl<T, R, C, N, M> IntoSuperset<C, crate::TyList!(N, @M)> for CoCons<T, R>
where
    C: Access<T, N>,
    R: IntoSuperset<C, M>,
{
    fn into_superset(self) -> C {
        match self {
            Self::Value(value) => Access::put(value),
            Self::Rest(rest) => rest.into_superset(),
        }
    }
}

impl<T: CoProd> IntoSubset<CoNil, crate::TyList!()> for T {
    type Remainder = Self;

    fn into_subset(self) -> Result<CoNil, Self::Remainder> { Err(self) }
}

impl<T, R: CoProd, C, N, M> IntoSubset<CoCons<T, R>, crate::TyList!(N, @M)> for C
where
    C: Access<T, N>,
    C::Remainder: IntoSubset<R, M>,
{
    type Remainder = <C::Remainder as IntoSubset<R, M>>::Remainder;

    fn into_subset(self) -> Result<CoCons<T, R>, Self::Remainder> {
        match self.take() {
            Ok(value) => Ok(CoCons::Value(value)),
            Err(rest) => rest.into_subset().map(CoCons::Rest),
        }
    }
}

impl<T: CoProd, U: CoProd, N> Shuffle<U, N> for T where T: IntoSubset<U, N, Remainder = CoNil> {}
pub trait Shuffle<T: CoProd, N>: IntoSubset<T, N, Remainder = CoNil> {
    fn shuffle(self) -> T {
        match self.into_subset() {
            Ok(x) => x,
            Err(x) => {
                let _: CoNil = x;
                match x {}
            }
        }
    }
}

pub trait AnonResult<Err, N> {
    type Ok;

    fn lift_err(self) -> Result<Self::Ok, Err>;
}

impl<T, E, F: CoProd, N> AnonResult<F, N> for Result<T, E>
where
    E: IntoSuperset<F, N>,
{
    type Ok = T;

    fn lift_err(self) -> Result<T, F> { self.map_err(IntoSuperset::into_superset) }
}