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}