use crate::{count_tokens, hcons::HEmpty, hcons_pat};
pub trait HList {
const LEN: usize;
fn len(&self) -> usize { Self::LEN }
fn is_empty(&self) -> bool { Self::LEN == 0 }
}
pub trait AsRefs<'a>: HList {
type Output: HList + 'a;
fn as_refs(&'a self) -> Self::Output;
}
pub trait AsMuts<'a>: HList {
type Output: HList + 'a;
fn as_muts(&'a mut self) -> Self::Output;
}
pub trait Extend<Other: HList>: HList {
type Output: HList;
fn extend(self, other: Other) -> Self::Output;
}
pub trait Fun<T: HList>: FunMut<T> {
fn call_fun(&self, args: T) -> Self::Output;
}
pub trait FunMut<T: HList>: FunOnce<T> {
fn call_fun_mut(&mut self, args: T) -> Self::Output;
}
pub trait FunOnce<T: HList> {
type Output;
fn call_fun_once(self, args: T) -> Self::Output;
}
pub trait IntoOpts: HList {
type Output: HList + Sized;
fn into_opts(self) -> Self::Output;
}
pub trait PushFront<E>: HList {
type Output: HList;
fn push_front(self, element: E) -> Self::Output;
}
pub trait PushBack<E>: HList {
type Output;
fn push_back(self, element: E) -> Self::Output;
}
pub trait Reverse: HList {
type Output;
fn rev(self) -> Self::Output;
}
pub trait TryUncons: HList + Sized {
type Head;
type Tail: HList;
fn try_uncons(self) -> Result<(Self::Head, Self::Tail), Self>;
}
pub trait TryValidateOpt: HList + Sized {
type Output;
fn try_validate(self) -> Result<Self::Output, Self>;
}
pub trait TryValidateRes: HList + Sized {
type Output;
fn try_validate(self) -> Result<Self::Output, Self>;
}
pub trait Uncons: HList {
type Head;
type Tail: HList;
fn uncons(self) -> (Self::Head, Self::Tail);
}
pub trait UnconsExt: Uncons {
fn into_head(self) -> Self::Head;
fn into_tail(self) -> Self::Tail;
}
pub trait UnconsOpt: HList {
type Head;
type Tail: HList;
fn uncons_opt(self) -> Option<(Self::Head, Self::Tail)>;
}
pub trait Unpack: HList {
type Output;
fn unpack(self) -> Self::Output;
}
pub trait ValidateOpt: HList {
type Output;
fn validate(self) -> Option<Self::Output>;
}
pub trait ValidateRes<E>: HList {
type Output;
fn validate(self) -> Result<Self::Output, E>;
}
pub trait PopBack {
type Back;
type Front: HList;
fn pop_back(self) -> (Self::Back, Self::Front);
}
pub trait TryPopBack {
type Back;
type Front: HList;
fn try_pop_back(self) -> Option<(Self::Back, Self::Front)>;
}
pub trait PopBackExt {
type Back;
type Front: HList;
fn into_back(self) -> Self::Back;
fn into_heads(self) -> Self::Front;
}
#[derive(Debug, PartialEq, Eq)]
pub enum Never {}
#[derive(Debug, PartialEq, Eq)]
pub enum NeverList {}
impl<Dst, Src> Extend<Dst> for Src
where
Src: Uncons,
Dst: HList,
Src::Tail: Extend<Dst>,
<Src::Tail as Extend<Dst>>::Output: PushFront<Src::Head>,
{
type Output = <<Src::Tail as Extend<Dst>>::Output as PushFront<Src::Head>>::Output;
fn extend(self, other: Dst) -> Self::Output {
let (h, t) = self.uncons();
t.extend(other).push_front(h)
}
}
impl<L> IntoOpts for L
where
L: Uncons,
L::Tail: IntoOpts,
<L::Tail as IntoOpts>::Output: PushFront<Option<L::Head>>,
{
type Output = <<L::Tail as IntoOpts>::Output as PushFront<Option<L::Head>>>::Output;
fn into_opts(self) -> Self::Output {
let (head, tail) = self.uncons();
tail.into_opts().push_front(Some(head))
}
}
impl<L, E> PushBack<E> for L
where
L: Uncons,
L::Tail: PushBack<E>,
<L::Tail as PushBack<E>>::Output: PushFront<L::Head>,
{
type Output = <<L::Tail as PushBack<E>>::Output as PushFront<L::Head>>::Output;
fn push_back(self, element: E) -> Self::Output {
let (h, t) = self.uncons();
t.push_back(element).push_front(h)
}
}
impl<L> Reverse for L
where
L: Uncons,
L::Tail: Reverse,
<L::Tail as Reverse>::Output: PushBack<L::Head>,
{
type Output = <<L::Tail as Reverse>::Output as PushBack<L::Head>>::Output;
fn rev(self) -> Self::Output {
let (h, t) = self.uncons();
t.rev().push_back(h)
}
}
macro_rules! impl_from_into_array {
() => {};
($head:ident $($tail:ident) *) => {
impl_from_into_array!($($tail) *);
impl<E, $head: Into<E>, $($tail: Into<E>), *> From<crate::HCons![$head, $($tail,) *]> for [E; { count_tokens!($head $($tail) *)}] {
#[allow(non_snake_case)]
fn from(value: crate::HCons![$head, $($tail,) *]) -> [E; crate::count_tokens!($head $($tail) *)] {
let hcons_pat!($head, $($tail,) *) = value;
[$head.into(), $($tail.into(),) *]
}
}
impl<E, $head, $($tail), *> From<[E; { count_tokens!($head $($tail) *)}]> for crate::HCons![$head, $($tail,) *]
where E: Into<$head> $(+ Into<$tail>) *
{
#[allow(non_snake_case)]
fn from(arr: [E; crate::count_tokens!($head $($tail) *)]) -> Self {
let [$head, $($tail,) *] = arr;
crate::hcons!($head.into(), $($tail.into(),) *)
}
}
};
}
impl_from_into_array!(T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 T12 T13 T14 T15 T16 T17 T18 T19 T20 T21 T22 T23 T24 T25 T26 T27 T28 T29 T30 T31 T32);
impl<T> From<[T; 0]> for HEmpty {
fn from(_: [T; 0]) -> HEmpty { HEmpty }
}
impl TryUncons for () {
type Head = Never;
type Tail = NeverList;
fn try_uncons(self) -> Result<(Self::Head, Self::Tail), Self> { Err(self) }
}
impl TryUncons for HEmpty {
type Head = Never;
type Tail = NeverList;
fn try_uncons(self) -> Result<(Self::Head, Self::Tail), Self> { Err(self) }
}
impl<T: Uncons> TryUncons for T {
type Head = T::Head;
type Tail = T::Tail;
fn try_uncons(self) -> Result<(Self::Head, Self::Tail), Self> { Ok(self.uncons()) }
}
impl<T: Uncons> UnconsExt for T {
fn into_head(self) -> Self::Head { self.uncons().0 }
fn into_tail(self) -> Self::Tail { self.uncons().1 }
}
impl HList for NeverList {
const LEN: usize = 0;
}
impl UnconsOpt for () {
type Head = Never;
type Tail = NeverList;
fn uncons_opt(self) -> Option<(Self::Head, Self::Tail)> { None }
}
impl UnconsOpt for HEmpty {
type Head = Never;
type Tail = NeverList;
fn uncons_opt(self) -> Option<(Self::Head, Self::Tail)> { None }
}
impl<T: Uncons> UnconsOpt for T {
type Head = T::Head;
type Tail = T::Tail;
fn uncons_opt(self) -> Option<(Self::Head, Self::Tail)> { Some(self.uncons()) }
}
impl<L, H> ValidateOpt for L
where
L: Uncons<Head = Option<H>>,
L::Tail: ValidateOpt,
<L::Tail as ValidateOpt>::Output: PushFront<H>,
{
type Output = <<L::Tail as ValidateOpt>::Output as PushFront<H>>::Output;
fn validate(self) -> Option<Self::Output> {
let (head, tail) = self.uncons();
head.and_then(|h| tail.validate().map(move |t| t.push_front(h)))
}
}
impl<L, H, E> ValidateRes<E> for L
where
L: Uncons<Head = Result<H, E>>,
L::Tail: ValidateRes<E>,
<L::Tail as ValidateRes<E>>::Output: PushFront<H>,
{
type Output = <<L::Tail as ValidateRes<E>>::Output as PushFront<H>>::Output;
fn validate(self) -> Result<Self::Output, E> {
let (head, tail) = self.uncons();
head.and_then(|h| tail.validate().map(move |t| t.push_front(h)))
}
}
impl<L, H> TryValidateOpt for L
where
L: Uncons<Head = Option<H>>,
L::Tail: TryValidateOpt + PushFront<L::Head, Output = L>,
<L::Tail as TryValidateOpt>::Output: PushFront<H>,
{
type Output = <<L::Tail as TryValidateOpt>::Output as PushFront<H>>::Output;
fn try_validate(self) -> Result<Self::Output, Self> {
let (head, tail) = self.uncons();
match head {
Some(h) => match tail.try_validate() {
Ok(t) => Ok(t.push_front(h)),
Err(t) => Err(t.push_front(Some(h))),
},
None => Err(tail.push_front(head)),
}
}
}
impl<L, H, E> TryValidateRes for L
where
L: Uncons<Head = Result<H, E>>,
L::Tail: TryValidateRes + PushFront<L::Head, Output = L>,
<L::Tail as TryValidateRes>::Output: PushFront<H>,
{
type Output = <<L::Tail as TryValidateRes>::Output as PushFront<H>>::Output;
fn try_validate(self) -> Result<Self::Output, Self> {
let (head, tail) = self.uncons();
match head {
Ok(h) => match tail.try_validate() {
Ok(t) => Ok(t.push_front(h)),
Err(t) => Err(t.push_front(Ok(h))),
},
_ => Err(tail.push_front(head)),
}
}
}
impl<L> PopBack for L
where
L: Reverse,
L::Output: Uncons,
<L::Output as Uncons>::Tail: Reverse,
<<L::Output as Uncons>::Tail as Reverse>::Output: HList,
{
type Back = <L::Output as Uncons>::Head;
type Front = <<L::Output as Uncons>::Tail as Reverse>::Output;
fn pop_back(self) -> (Self::Back, Self::Front) {
let (b, h) = self.rev().uncons();
(b, h.rev())
}
}
impl<L> TryPopBack for L
where
L: PopBack,
{
type Back = L::Back;
type Front = L::Front;
fn try_pop_back(self) -> Option<(Self::Back, Self::Front)> { Some(self.pop_back()) }
}
impl<L> PopBackExt for L
where
L: PopBack,
{
type Back = L::Back;
type Front = L::Front;
fn into_back(self) -> Self::Back { self.pop_back().0 }
fn into_heads(self) -> Self::Front { self.pop_back().1 }
}