1use std::ops::Add;
2
3pub trait HList: Sized {
4 fn length(&self) -> u32;
5
6 fn push<H>(self, h: H) -> HCons<H, Self> {
7 HCons {
8 head: h,
9 tail: self,
10 }
11 }
12}
13
14#[derive(PartialEq, Eq, Debug)]
26pub struct HNil;
27
28impl HList for HNil {
29 fn length(&self) -> u32 {
30 0
31 }
32}
33
34#[derive(PartialEq, Eq, Debug)]
39pub struct HCons<H, T> {
40 pub head: H,
41 pub tail: T,
42}
43
44impl<H, T: HList> HList for HCons<H, T> {
45 fn length(&self) -> u32 {
46 1 + self.tail.length()
47 }
48}
49
50impl<H, T> HCons<H, T> {
51 pub fn pop(self) -> (H, T) {
63 (self.head, self.tail)
64 }
65}
66
67
68pub fn h_cons<H, T: HList>(h: H, tail: T) -> HCons<H, T> {
82 tail.push(h)
83}
84
85#[macro_export]
101macro_rules! hlist {
102
103 () => { HNil };
105
106 ($single: expr) => {
108 HCons { head: $single, tail: HNil }
109 };
110
111 ($first: expr, $( $repeated: expr ), +) => {
112HCons { head: $first, tail: hlist!($($repeated), *)}
115 };
116
117}
118
119impl<RHS> Add<RHS> for HNil
120 where RHS: HList
121{
122 type Output = RHS;
123
124 fn add(self, rhs: RHS) -> RHS {
125 rhs
126 }
127}
128
129impl<H, T, RHS> Add<RHS> for HCons<H, T>
130 where T: Add<RHS>,
131 RHS: HList
132{
133 type Output = HCons<H, <T as Add<RHS>>::Output>;
134
135 fn add(self, rhs: RHS) -> Self::Output {
136 HCons {
137 head: self.head,
138 tail: self.tail + rhs,
139 }
140 }
141}
142
143pub trait IntoTuple2 {
144 type HeadType;
145 type TailOutput;
146
147 fn into_tuple2(self) -> (Self::HeadType, Self::TailOutput);
164}
165
166impl<T> IntoTuple2 for HCons<T, HNil> {
167 type HeadType = T;
168 type TailOutput = HNil;
169
170 fn into_tuple2(self) -> (Self::HeadType, Self::TailOutput) {
171 (self.head, HNil)
172 }
173}
174
175impl<T, Tail> IntoTuple2 for HCons<T, Tail>
176 where Tail: IntoTuple2
177{
178 type HeadType = T;
179 type TailOutput = (<Tail as IntoTuple2>::HeadType, <Tail as IntoTuple2>::TailOutput);
180
181 fn into_tuple2(self) -> (Self::HeadType, Self::TailOutput) {
182 (self.head, self.tail.into_tuple2())
183 }
184}
185
186#[cfg(test)]
187mod tests {
188
189 use super::*;
190
191 #[test]
192 fn test_hcons() {
193 let hlist1 = h_cons(1, HNil);
194 let (h, _) = hlist1.pop();
195 assert_eq!(h, 1);
196
197 let hlist2 = h_cons("hello", h_cons(1, HNil));
198 let (h2, tail2) = hlist2.pop();
199 let (h1, _) = tail2.pop();
200 assert_eq!(h2, "hello");
201 assert_eq!(h1, 1);
202 }
203
204 struct HasHList<T: HList>(T);
205
206 #[test]
207 fn test_contained_list() {
208 let c = HasHList(h_cons(1, HNil));
209 let retrieved = c.0;
210 assert_eq!(retrieved.length(), 1);
211 let new_list = h_cons(2, retrieved);
212 assert_eq!(new_list.length(), 2);
213 }
214
215 #[test]
216 fn test_macro() {
217 assert_eq!(hlist![], HNil);
218 let h = hlist![1, "2", 3];
219 let (h1, tail1) = h.pop();
220 assert_eq!(h1, 1);
221 assert_eq!(tail1, hlist!["2", 3]);
222 let (h2, tail2) = tail1.pop();
223 assert_eq!(h2, "2");
224 assert_eq!(tail2, hlist![3]);
225 let (h3, tail3) = tail2.pop();
226 assert_eq!(h3, 3);
227 assert_eq!(tail3, HNil);
228 }
229
230 #[test]
231 fn test_add() {
232 let h1 = hlist![true, "hi"];
233 let h2 = hlist![1, 32f32];
234 let combined = h1 + h2;
235 assert_eq!(combined, hlist![true, "hi", 1, 32f32])
236 }
237
238}