veecle_os_runtime/
cons.rs

1//! Helper traits to work with type-level [cons-lists](https://en.wikipedia.org/wiki/Cons#Lists).
2//!
3//! These are useful when working with macros and traits that need to support arbitrary length type
4//! lists, without having to macro generate hundreds of trait implementations for different length
5//! tuples.
6//!
7//! Rather than using raw tuples `type Nil = (); type Cons<T, U> = (T, U);` this includes new
8//! nominal types so that we can have safe pin-projection.
9
10/// The terminal element of a cons-list.
11#[derive(Debug, Eq, PartialEq, Copy, Clone)]
12pub struct Nil;
13
14/// Prepends an element to the cons-list, somewhat equivalent to the array `[T, ...U]`.
15#[pin_project::pin_project]
16#[derive(Debug, Eq, PartialEq, Copy, Clone)]
17pub struct Cons<T, U>(#[pin] pub T, #[pin] pub U);
18
19/// Internal helper that asserts post-normalization types are the same, see usage in below doc-tests.
20#[doc(hidden)]
21#[macro_export]
22macro_rules! __assert_same_type {
23    (
24        for<$($generic:ident),*>
25        $type1:ty,
26        $type2:ty $(,)?
27    ) => {
28        const _: () = {
29            fn equivalent<$($generic,)*>(value: $type1) -> $type2 { value }
30        };
31    };
32}
33
34/// Converts a tuple-based cons-list into one using our nominal types.
35///
36/// ```rust
37/// use veecle_os_runtime::__assert_same_type;
38/// use veecle_os_runtime::__exports::{Cons, Nil, TupleConsToCons};
39///
40/// __assert_same_type! {
41///     for<>
42///     <() as TupleConsToCons>::Cons,
43///     Nil,
44/// }
45///
46/// __assert_same_type! {
47///     for<A, B, C>
48///     <(A, (B, (C, ()))) as TupleConsToCons>::Cons,
49///     Cons<A, Cons<B, Cons<C, Nil>>>,
50/// }
51/// ```
52pub trait TupleConsToCons {
53    /// The [`Cons`]-based cons-list
54    type Cons;
55}
56
57impl TupleConsToCons for () {
58    type Cons = Nil;
59}
60
61impl<T, U> TupleConsToCons for (T, U)
62where
63    U: TupleConsToCons,
64{
65    type Cons = Cons<T, <U as TupleConsToCons>::Cons>;
66}
67
68/// Given a list of types or values, generate a cons-list for those types or values.
69///
70/// ```rust
71/// use veecle_os_runtime::{__assert_same_type, __make_cons};
72/// use veecle_os_runtime::__exports::{Cons, Nil};
73///
74/// __assert_same_type! {
75///     for<>
76///     __make_cons!(@type),
77///     Nil,
78/// }
79///
80/// __assert_same_type! {
81///     for<A, B, C>
82///     __make_cons!(@type A, B, C),
83///     Cons<A, Cons<B, Cons<C, Nil>>>,
84/// }
85///
86/// assert_eq! {
87///     __make_cons!(@value),
88///     Nil,
89/// }
90///
91/// assert_eq! {
92///     __make_cons!(@value 1u32, "hello ferris", 3.141594f64),
93///     Cons(1, Cons("hello ferris", Cons(3.141594, Nil))),
94/// }
95/// ```
96#[doc(hidden)]
97#[macro_export]
98macro_rules! __make_cons {
99    (@type) => {
100        $crate::__exports::Nil
101    };
102
103    (@type $first:ty $(, $rest:ty)* $(,)? ) => {
104        $crate::__exports::Cons<$first, $crate::__make_cons!(@type $($rest,)*)>
105    };
106
107    (@value) => {
108        $crate::__exports::Nil
109    };
110
111    (@value $first:expr $(, $rest:expr)* $(,)? ) => {
112        $crate::__exports::Cons($first, $crate::__make_cons!(@value $($rest,)*))
113    };
114}
115
116/// Given a cons-list value, and a depth denoted by a series of any kind of token-tree, read the value at that depth
117/// from the list.
118///
119/// ```rust
120/// use veecle_os_runtime::{__make_cons, __read_cons};
121///
122/// let cons = __make_cons!(@value 1u32, "hello ferris", 3.141594f64);
123///
124/// assert_eq! {
125///     __read_cons! {
126///         from: cons,
127///         depth: [],
128///     },
129///     1u32,
130/// }
131///
132/// assert_eq! {
133///     __read_cons! {
134///         from: cons,
135///         depth: [()],
136///     },
137///     "hello ferris",
138/// }
139///
140/// assert_eq! {
141///     __read_cons! {
142///         from: cons,
143///         depth: [() ()],
144///     },
145///     3.141594f64,
146/// }
147/// ```
148#[doc(hidden)]
149#[macro_export]
150macro_rules! __read_cons {
151    (
152        from: $from:expr,
153        depth: [$($depth:tt)*],
154    ) => {
155        $crate::__read_cons! {
156            depth: [$($depth)*],
157            result: [$from],
158        }
159    };
160
161    // Once at the final depth, read the value stored in the first field of the current element.
162    (
163        depth: [],
164        result: [$($result:tt)*],
165    ) => {
166        $($result)* . 0
167    };
168
169    // Discard one token from the head of the depth, and index into the second field of the current element.
170    (
171        depth: [$_:tt $($rest:tt)*],
172        result: [$($result:tt)*],
173    ) => {
174        $crate::__read_cons! {
175            depth: [$($rest)*],
176            result: [$($result)* . 1],
177        }
178    };
179}