ct_utils/
ct_cons.rs

1//! This module defines a cons list for types, [`CTCons`].
2//! The cons list can be queried and the result is calculated at compile time.
3//! See [`CTSized`] and [`CTOffset`] for more information about querying the cons list.
4//!
5//! # Example
6//! ```rust
7//! extern crate ct_utils;
8//! use ct_utils::prelude::*;
9//! use ct_utils::ct_cons::Cons;
10//!
11//! fn main() {
12//!     type ExampleOne = Cons<Cons<Cons<CTConsTerm, u8>, u32>, u64>;
13//!     //
14//!     type ExampleTwo = <<CTConsTerm as CTAppend<u8>>::Output as CTAppend<u32>>::Output;
15//! }
16//! ```
17
18use ct_if::IfCheck;
19use std::marker::PhantomData;
20use std::ops::Add;
21use typenum::{U0, U1, U1024, Unsigned};
22
23/// This value is returned when querying [`CTOffset`] and the requested type is not
24/// found within the subject [`CTCons`].
25pub type CTConsOffsetInvalid = U1024;
26
27/// Represents a cons list where each new addition builds further onto the previous list.
28/// The previous list is at [`CTCons::Tail`] and [`CTConsTerm`] is used as bootstrapper
29/// to create the initial tail.
30/// The latest item is at [`CTCons::Head`].
31///
32/// Use [`CTAppend`] to add new types to the cons list.
33///
34/// # Preconditions
35/// -   The cons list MUST NOT contain multiple items of the exact same type. This will result
36///     in wrong offsets.
37///     Type X and Type Y are considered exactly the same if their TypeId match. eg
38///     `std::any::TypeId::of::<X> == std::any::TypeId::of::<Y>`
39///     See [`std::any::TypeId`].
40///
41pub trait CTCons {
42    /// The head of the list. This is exactly one item.
43    type Head: 'static;
44    /// This is the parent of the implemented type. The tail contains all items except the Head
45    /// of the implemented type.
46    type Tail: CTCons + 'static;
47}
48
49#[doc(hidden)]
50// Trait used to count the amount of items within [`CTCons`], but due to constraints must be publicly
51// exported.
52// Use the trait [`CTSized`] to query the size of [`CTCons`].
53pub trait CTCounter {
54    /// A type defined by [`typenum`] which represents an unsigned integer.
55    /// On this type is also addition with 1u implemented.
56    ///
57    /// # Note
58    /// The only operation possible on this type is addition and creation of a corresponding value.
59    type Counter: Unsigned + Add<U1>;
60}
61
62/// Trait used to append items to compile time structure.
63///
64/// Cast the parent structure as [`CTAppend`] whith the addition as generic argument.
65/// If this trait is implemented for the parent structure, [`CTAppend::Output`] will
66/// hold the resulting type.
67///
68/// # Example
69/// ```rust
70/// # extern crate ct_utils;
71/// # use ct_utils::prelude::*;
72/// # fn main() {
73/// type ExampleTwo = <<CTConsTerm as CTAppend<u8>>::Output as CTAppend<u32>>::Output;
74/// # }
75/// ```
76pub trait CTAppend<X> {
77    /// The new compile time structure which consists of the implemented structure
78    /// with X as new item.
79    type Output: CTCons;
80}
81
82/// Trait used to measure the amount of items within compile time structures.
83///
84/// Cast the structure as [`CTSized`]. If this trait is implemented, you can query the
85/// size from [`CTSized::Size`].
86/// Alternatively a value can be constructed from the type representing the size, just
87/// call [`CTSized::size`].
88///
89/// # Example
90/// ```rust
91/// # extern crate ct_utils;
92/// # use ct_utils::prelude::*;
93/// # use ct_utils::ct_cons::Cons;
94/// # fn main() {
95/// let size = <Cons<Cons<Cons<CTConsTerm, u8>, u32>, u64> as CTSized>::size();
96/// assert_eq!(size, 3);
97/// # }
98/// ```
99pub trait CTSized {
100    /// The size representation of the implemented compile time structure.
101    type Size: Unsigned;
102
103    /// Returns the amount of items added to the implemented compile time structure.
104    fn size() -> usize;
105}
106
107/// Trait used to calculate the 0-indexed offset of a specific type item within
108/// compile time structures.
109///
110/// Cast the structure as [`CTOffset`]. If this trait is implemented, you can query the
111/// offset of the requested item from [`CTOffset::Offset`].
112/// Alternatively a value can be constructed representing the offset, just call
113/// [`CTOffset::offset`].
114///
115/// ** Preferably use the [`CTOffsetExt`] trait! **
116///
117/// # Note
118/// If the requested type cannot be found within the implemented structure,
119/// [`CTConsOffsetInvalid`] is returned.
120///
121/// # Example
122/// ```rust
123/// # extern crate ct_utils;
124/// # use ct_utils::prelude::*;
125/// # use ct_utils::ct_cons::Cons;
126/// # use ct_utils::ct_cons::CTOffset;
127/// # fn main() {
128/// let offset = <Cons<Cons<Cons<CTConsTerm, u8>, u32>, u64> as CTOffset<u64>>::offset();
129/// assert_eq!(offset, 2);    // Note: Offset is 0-indexed!
130/// # }
131/// ```
132pub trait CTOffset<Target> {
133    /// The offset representation of the type Target within the implemented compile time
134    /// structure.
135    ///
136    /// # Note
137    /// If the type was NOT found within the implemented ct structure, [`CTConsOffsetInvalid`]
138    /// is provided.
139    type Offset: Unsigned + Add<U1>;
140
141    /// Returns a value representing the offset within the compile time structure.
142    fn offset() -> usize;
143}
144
145/// Trait used to calculate the offset of a specific type without upcasting to the
146/// [`CTOffset`] trait.
147///
148/// # Example
149/// ```rust
150/// # extern crate ct_utils;
151/// # use ct_utils::prelude::*;
152/// # use ct_utils::ct_cons::Cons;
153/// type BigList = Cons<Cons<Cons<CTConsTerm, u8>, u32>, u64>;
154/// # fn main() {
155/// let offset = BigList::offset_of::<u64>();
156/// assert_eq!(offset, 2);    // Note: Offset is 0-indexed!
157/// # }
158/// ```
159pub trait CTOffsetExt {
160    /// Returns the offset of the provided type within the implemented compile time
161    /// structure.
162    fn offset_of<X>() -> usize
163    where
164        Self: CTOffset<X>;
165}
166
167/// Termination type for [`Cons`].
168///
169/// Use this type to construct a new cons list ([`CTCons`]). See [`CTAppend`] for more
170/// information.
171pub enum CTConsTerm {}
172
173/// Actual type used to carry cons list information.
174///
175/// This type is useful to create a new cons list in a compact syntax, like the example
176/// within the module documentation. Other than for creating new cons lists this structure
177/// isn't useful.
178pub struct Cons<Tail, Head>
179where
180    Tail: CTCons,
181{
182    _marker: PhantomData<(Tail, Head)>,
183}
184
185ctif_specialize!{
186    /// Specialized implementation of [`IfCheck`] to accomodate for the additional constraints
187    /// necessary on [`CTIf::Path`].
188    trait_name = CTIfOffset,
189    conditions = [Unsigned, Add<U1>]
190}
191
192impl CTCons for CTConsTerm {
193    type Head = ();
194    type Tail = CTConsTerm;
195}
196
197impl<X> CTAppend<X> for CTConsTerm
198where
199    X: 'static,
200{
201    type Output = Cons<CTConsTerm, X>;
202}
203
204impl CTCounter for CTConsTerm {
205    type Counter = U0;
206}
207
208impl<Target> CTOffset<Target> for CTConsTerm {
209    type Offset = CTConsOffsetInvalid;
210
211    fn offset() -> usize {
212        Self::Offset::to_usize()
213    }
214}
215
216impl<Tail, Head> CTCons for Cons<Tail, Head>
217where
218    Tail: CTCons + 'static,
219    Head: 'static,
220{
221    type Head = Head;
222    type Tail = Tail;
223}
224
225impl<X, Tail, Head> CTAppend<X> for Cons<Tail, Head>
226where
227    X: 'static,
228    Tail: CTCons + 'static,
229    Head: 'static,
230{
231    type Output = Cons<Cons<Tail, Head>, X>;
232}
233
234impl<Tail, Head> CTCounter for Cons<Tail, Head>
235where
236    Tail: CTCounter + CTCons,
237    <<Tail as CTCounter>::Counter as Add<U1>>::Output: Unsigned + Add<U1>,
238{
239    type Counter = <Tail::Counter as Add<U1>>::Output;
240}
241
242impl<Tail, Head> CTSized for Cons<Tail, Head>
243where
244    Tail: CTCons,
245    Self: CTCounter,
246{
247    type Size = <Self as CTCounter>::Counter;
248
249    fn size() -> usize {
250        Self::Size::to_usize()
251    }
252}
253
254impl<Target, Tail, Head> CTOffset<Target> for Cons<Tail, Head>
255where
256    Tail: CTCons + CTOffset<Target> + CTCounter,
257{
258    type Offset = <IfCheck<Head> as CTIfOffset<Target, Tail::Counter, Tail::Offset>>::Path;
259
260    fn offset() -> usize {
261        Self::Offset::to_usize()
262    }
263}
264
265impl<C> CTOffsetExt for C
266where
267    C: CTCons,
268{
269    fn offset_of<X>() -> usize
270    where
271        C: CTOffset<X>,
272    {
273        C::offset()
274    }
275}
276
277#[cfg(test)]
278mod test {
279    use super::*;
280
281    type Single = Cons<CTConsTerm, i32>;
282    type Middle = Cons<Cons<Cons<CTConsTerm, i32>, u32>, f32>;
283    type Big = Cons<Cons<Cons<Cons<Cons<CTConsTerm, i32>, u32>, f32>, i64>, usize>;
284
285    #[test]
286    fn sized_single() {
287        let size = Single::size();
288        assert_eq!(size, 1);
289    }
290
291    #[test]
292    fn sized_big() {
293        let size = Big::size();
294        assert_eq!(size, 5);
295    }
296
297    fn offset_for<CTCons, Target>() -> usize
298    where
299        CTCons: CTOffset<Target>,
300    {
301        CTCons::offset()
302    }
303
304    #[test]
305    fn offset_first() {
306        let offset = offset_for::<Single, i32>();
307        assert_eq!(offset, 0);
308    }
309
310    #[test]
311    fn offset_middle() {
312        let offset = offset_for::<Middle, u32>();
313        assert_eq!(offset, 1);
314    }
315
316    #[test]
317    fn offset_end() {
318        let offset = offset_for::<Big, usize>();
319        assert_eq!(offset, 4);
320    }
321
322    #[test]
323    fn offset_unknown() {
324        let offset = offset_for::<Single, f32>();
325        assert_eq!(offset, CTConsOffsetInvalid::to_usize());
326    }
327}