Skip to main content

pl_hlist/
hlist.rs

1//
2// Copyright (c) 2015-2019 Plausible Labs Cooperative, Inc.
3// All rights reserved.
4//
5// This implementation based on List type from:
6//   https://github.com/epsilonz/shoggoth.rs
7//
8
9/// A heterogeneous list that can hold elements of different types.
10pub trait HList {
11    /// Creates a new `HCons` with the given `X` value in head position.
12    fn cons<X>(self, x: X) -> HCons<X, Self>
13    where
14        Self: Sized,
15    {
16        HCons(x, self)
17    }
18}
19
20/// An empty `HList` used as the terminal element.
21#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
22pub struct HNil;
23
24impl HList for HNil {}
25
26/// The "cons" of a head element of type `H` and a tail `HList`.
27#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
28pub struct HCons<H, T: HList>(pub H, pub T);
29
30impl<H, T: HList> HCons<H, T> {
31    /// Returns a reference to the head element of this list.
32    pub fn head(&self) -> &H {
33        &self.0
34    }
35
36    /// Returns a reference to the tail of this list.
37    pub fn tail(&self) -> &T {
38        &self.1
39    }
40}
41
42impl<H, T: HList> HList for HCons<H, T> {}
43
44/// Allows for conversion from an `HList` to an instance of the `Self` type.
45pub trait FromHList<H>
46where
47    H: HList,
48{
49    fn from_hlist(hlist: H) -> Self;
50}
51
52/// Allows for copying the contents of `Self` into an `HList`.
53pub trait ToHList<H>
54where
55    H: HList,
56{
57    fn to_hlist(&self) -> H;
58}
59
60/// Allows for converting (and consuming) `Self` into an `HList`.
61pub trait IntoHList<H>
62where
63    H: HList,
64{
65    fn into_hlist(self) -> H;
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71    use pl_hlist_derive::HListSupport;
72
73    #[test]
74    fn head_should_work() {
75        let hlist = HCons(1u8, HNil);
76        assert_eq!(*hlist.head(), 1u8);
77    }
78
79    #[test]
80    fn tail_should_work() {
81        let hlist = HCons(1u8, HNil);
82        assert_eq!(*hlist.tail(), HNil);
83    }
84
85    #[test]
86    fn hlist_macros_should_work() {
87        {
88            let hlist1 = HNil;
89            let hlist2 = hlist!();
90            assert_eq!(hlist1, hlist2);
91        }
92
93        {
94            let hlist1 = HCons(1u8, HNil);
95            let hlist2 = hlist!(1u8);
96            assert_eq!(hlist1, hlist2);
97        }
98
99        {
100            let hlist1 = HCons(1u8, HCons(2i32, HCons("three", HNil)));
101            let hlist2 = hlist!(1u8, 2i32, "three");
102            assert_eq!(hlist1, hlist2);
103        }
104    }
105
106    #[derive(Debug, PartialEq, Eq, Clone, HListSupport)]
107    struct TestInnerStruct {
108        f1: u8,
109        f2: u8,
110    }
111
112    #[derive(Debug, PartialEq, Eq, Clone, HListSupport)]
113    struct TestStruct {
114        byte_field: u8,
115        inner_struct: TestInnerStruct,
116    }
117
118    #[test]
119    fn converting_struct_to_from_hlist_should_work() {
120        {
121            let s = TestInnerStruct::from_hlist(hlist!(1u8, 2u8));
122            assert_eq!(s.f1, 1u8);
123            assert_eq!(s.f2, 2u8);
124            let hlist0 = s.to_hlist();
125            assert_eq!(hlist0, hlist!(1u8, 2u8));
126            let hlist1 = s.into_hlist();
127            assert_eq!(hlist0, hlist1);
128        }
129
130        {
131            let s =
132                TestStruct::from_hlist(hlist!(7u8, TestInnerStruct::from_hlist(hlist!(1u8, 2u8))));
133            assert_eq!(s.byte_field, 7u8);
134            assert_eq!(s.inner_struct, TestInnerStruct { f1: 1, f2: 2 });
135            let hlist0 = s.to_hlist();
136            assert_eq!(hlist0, hlist!(7u8, TestInnerStruct { f1: 1, f2: 2 }));
137            let hlist1 = s.into_hlist();
138            assert_eq!(hlist0, hlist1);
139        }
140    }
141}