use std::ops::Add;
pub trait HList: Sized {
fn length(&self) -> u32;
fn push<H>(self, h: H) -> HCons<H, Self> {
HCons {
head: h,
tail: self,
}
}
}
#[derive(PartialEq, Eq, Debug)]
pub struct HNil;
impl HList for HNil {
fn length(&self) -> u32 {
0
}
}
#[derive(PartialEq, Eq, Debug)]
pub struct HCons<H, T> {
pub head: H,
pub tail: T,
}
impl<H, T: HList> HList for HCons<H, T> {
fn length(&self) -> u32 {
1 + self.tail.length()
}
}
impl<H, T> HCons<H, T> {
pub fn pop(self) -> (H, T) {
(self.head, self.tail)
}
}
pub fn h_cons<H, T: HList>(h: H, tail: T) -> HCons<H, T> {
tail.push(h)
}
#[macro_export]
macro_rules! hlist {
() => { HNil };
($single: expr) => {
HCons { head: $single, tail: HNil }
};
($first: expr, $( $repeated: expr ), +) => {
HCons { head: $first, tail: hlist!($($repeated), *)}
};
}
impl<RHS> Add<RHS> for HNil
where RHS: HList
{
type Output = RHS;
fn add(self, rhs: RHS) -> RHS {
rhs
}
}
impl<H, T, RHS> Add<RHS> for HCons<H, T>
where T: Add<RHS>,
RHS: HList
{
type Output = HCons<H, <T as Add<RHS>>::Output>;
fn add(self, rhs: RHS) -> Self::Output {
HCons {
head: self.head,
tail: self.tail + rhs,
}
}
}
pub trait IntoTuple2 {
type HeadType;
type TailOutput;
fn into_tuple2(self) -> (Self::HeadType, Self::TailOutput);
}
impl<T> IntoTuple2 for HCons<T, HNil> {
type HeadType = T;
type TailOutput = HNil;
fn into_tuple2(self) -> (Self::HeadType, Self::TailOutput) {
(self.head, HNil)
}
}
impl<T, Tail> IntoTuple2 for HCons<T, Tail>
where Tail: IntoTuple2
{
type HeadType = T;
type TailOutput = (<Tail as IntoTuple2>::HeadType, <Tail as IntoTuple2>::TailOutput);
fn into_tuple2(self) -> (Self::HeadType, Self::TailOutput) {
(self.head, self.tail.into_tuple2())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_hcons() {
let hlist1 = h_cons(1, HNil);
let (h, _) = hlist1.pop();
assert_eq!(h, 1);
let hlist2 = h_cons("hello", h_cons(1, HNil));
let (h2, tail2) = hlist2.pop();
let (h1, _) = tail2.pop();
assert_eq!(h2, "hello");
assert_eq!(h1, 1);
}
struct HasHList<T: HList>(T);
#[test]
fn test_contained_list() {
let c = HasHList(h_cons(1, HNil));
let retrieved = c.0;
assert_eq!(retrieved.length(), 1);
let new_list = h_cons(2, retrieved);
assert_eq!(new_list.length(), 2);
}
#[test]
fn test_macro() {
assert_eq!(hlist![], HNil);
let h = hlist![1, "2", 3];
let (h1, tail1) = h.pop();
assert_eq!(h1, 1);
assert_eq!(tail1, hlist!["2", 3]);
let (h2, tail2) = tail1.pop();
assert_eq!(h2, "2");
assert_eq!(tail2, hlist![3]);
let (h3, tail3) = tail2.pop();
assert_eq!(h3, 3);
assert_eq!(tail3, HNil);
}
#[test]
fn test_add() {
let h1 = hlist![true, "hi"];
let h2 = hlist![1, 32f32];
let combined = h1 + h2;
assert_eq!(combined, hlist![true, "hi", 1, 32f32])
}
}