Skip to main content

pipe_it/
dep.rs

1use std::marker::PhantomData;
2
3/// Static dependency container (Heterogeneous List).
4///
5/// This structure allows storing mixed types of dependencies and accessing them
6/// in a zero-cost manner (offsets calculated at compile time).
7#[derive(Debug, Clone, Copy, Default)]
8pub struct Dep<Head, Tail>(pub Head, pub Tail);
9impl Dep<(), ()> {
10    pub fn new() -> Self {
11        Self((), ())
12    }
13}
14
15impl<Head, Tail> Dep<Head, Tail> {
16    /// Prepend a new dependency to the list.
17    pub fn add<T>(self, item: T) -> Dep<T, Self> {
18        Dep(item, self)
19    }
20
21    /// Access a dependency by type.
22    ///
23    /// The index `I` is automatically inferred by the compiler.
24    /// This method shadows the trait method to provide better type inference.
25    #[inline(always)]
26    pub fn get<T, I>(&self) -> &T
27    where
28        Self: Has<T, I>,
29    {
30        Has::<T, I>::get(self)
31    }
32
33    /// access a mutable dependency by type.
34    #[inline(always)]
35    pub fn get_mut<T, I>(&mut self) -> &mut T
36    where
37        Self: Has<T, I>,
38    {
39        Has::<T, I>::get_mut(self)
40    }
41}
42/// Type-level index indicating the item is found Here (at the head).
43pub struct Here;
44
45/// Type-level index indicating the item is found There (in the tail).
46pub struct There<INDEX>(PhantomData<INDEX>);
47
48/// Trait to extract a dependency `T` using a specific `Index`.
49///
50/// The `Index` type (Here or There) guides the compiler to the correct field.
51pub trait Has<T, Index = ()> {
52    fn get(&self) -> &T;
53    fn get_mut(&mut self) -> &mut T;
54}
55
56/// Base case: The item matches the Head.
57impl<T, Tail> Has<T, Here> for Dep<T, Tail> {
58    #[inline(always)]
59    fn get(&self) -> &T {
60        &self.0
61    }
62
63    #[inline(always)]
64    fn get_mut(&mut self) -> &mut T {
65        &mut self.0
66    }
67}
68
69/// Recursive case: The item is not in Head, look into Tail.
70impl<Head, Tail, T, Index> Has<T, There<Index>> for Dep<Head, Tail>
71where
72    Tail: Has<T, Index>,
73{
74    #[inline(always)]
75    fn get(&self) -> &T {
76        self.1.get()
77    }
78
79    #[inline(always)]
80    fn get_mut(&mut self) -> &mut T {
81        self.1.get_mut()
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use crate::dep::{Dep};
88
89    #[test]
90    fn test_dep() {
91        #[derive(Debug, PartialEq)]
92        struct A(i32);
93        #[derive(Debug, PartialEq)]
94        struct B(&'static str);
95        #[derive(Debug, PartialEq)]
96        struct C([i32; 4]);
97
98        let mut dep = Dep::new().add(A(42)).add(B("hello")).add(C([1, 2, 3, 4]));
99
100        // Note: The order is reversed because we prepend: C is Head, B is next, A is last.
101        // However, the access is type-based, so order doesn't affect `get` usage as long as types are unique.
102
103        // Access A
104        assert_eq!(dep.get::<A, _>(), &A(42));
105        // Or even simpler if type is known from context:
106        let a: &A = dep.get();
107        assert_eq!(a, &A(42));
108
109        // Access B
110        assert_eq!(dep.get::<B, _>(), &B("hello"));
111
112        // Access C
113        assert_eq!(dep.get::<C, _>().0[0], 1);
114
115        // Mutable access
116        dep.get_mut::<A, _>().0 = 100;
117        assert_eq!(dep.get::<A, _>().0, 100);
118    }
119}