object_chain/
lib.rs

1//! Create static chains of objects with different types.
2//!
3//! In general, the chain starts (or ends, depending on your view) with a `Chain` element
4//! and is built up from any number of `Link`s.
5//!
6//! This basic structure only allows you
7//! to query the number of elements, but you can implement a more useful trait for both `Link` and
8//! `Chain` to make this structure more useful. For an example, check the
9//! `test_accessing_elements_with_common_interface` test in the source code.
10#![no_std]
11mod private {
12    pub trait Sealed {}
13
14    impl<V> Sealed for super::Chain<V> {}
15    impl<V, C: super::ChainElement> Sealed for super::Link<V, C> {}
16}
17
18/// A generic chain element
19pub trait ChainElement: private::Sealed {
20    type Inner;
21    type Parent;
22
23    /// Append an object to the chain
24    #[inline]
25    fn append<T>(self, item: T) -> Link<T, Self>
26    where
27        Self: Sized,
28    {
29        Link {
30            object: item,
31            parent: self,
32        }
33    }
34
35    /// Return the number of objects linked to this chain element
36    fn len(&self) -> usize;
37
38    fn get(&self) -> &Self::Inner;
39
40    fn get_mut(&mut self) -> &mut Self::Inner;
41
42    fn pop(self) -> (Self::Inner, Self::Parent);
43}
44
45/// This piece of the chain contains some object
46#[derive(Clone, Copy)]
47pub struct Link<V, C>
48where
49    C: ChainElement,
50{
51    /// The rest of the object chain
52    pub parent: C,
53
54    /// The current object
55    pub object: V,
56}
57
58impl<V, VC> ChainElement for Link<V, VC>
59where
60    VC: ChainElement,
61{
62    type Inner = V;
63    type Parent = VC;
64
65    #[inline]
66    fn len(&self) -> usize {
67        self.parent.len() + 1
68    }
69
70    fn get(&self) -> &Self::Inner {
71        &self.object
72    }
73
74    fn get_mut(&mut self) -> &mut Self::Inner {
75        &mut self.object
76    }
77
78    fn pop(self) -> (Self::Inner, Self::Parent) {
79        (self.object, self.parent)
80    }
81}
82
83/// This piece marks the end of a chain.
84#[derive(Clone, Copy)]
85pub struct Chain<V> {
86    /// The wrapped object.
87    pub object: V,
88}
89
90impl<V> Chain<V> {
91    /// Creates a new [`Chain`] by wrapping the given object.
92    pub const fn new(object: V) -> Self {
93        Self { object }
94    }
95}
96
97impl<V> ChainElement for Chain<V> {
98    type Inner = V;
99    type Parent = ();
100
101    #[inline]
102    fn len(&self) -> usize {
103        1
104    }
105
106    fn get(&self) -> &Self::Inner {
107        &self.object
108    }
109
110    fn get_mut(&mut self) -> &mut Self::Inner {
111        &mut self.object
112    }
113
114    fn pop(self) -> (Self::Inner, Self::Parent) {
115        (self.object, ())
116    }
117}
118
119/// Internal implementation of chain macro
120#[doc(hidden)]
121#[macro_export(local_inner_macros)]
122macro_rules! chain_impl {
123    ($x:ty) => {
124        Chain<$x>
125    };
126    ($x:ty,) => {
127        Chain<$x>
128    };
129    ($x:ty, $($rest:tt)+) => {
130        Link<$x, chain_impl! { $($rest)+ }>
131    };
132}
133
134/// Reverse the argument list to generate object chain
135#[doc(hidden)]
136#[macro_export(local_inner_macros)]
137macro_rules! reverse {
138    ([] $($reversed:tt)+) => {
139        chain_impl! { $($reversed)+ }
140    };
141    ([$first:ty] $($reversed:tt)*) => {
142        reverse! { [ ] $first, $($reversed)* }
143    };
144    ([$first:ty, $($rest:ty),*] $($reversed:tt)*) => {
145        reverse! { [ $($rest),* ] $first, $($reversed)* }
146    };
147}
148
149/// Creates an object chain from the argument types.
150///
151/// Using this macro is completely optional but it reduces the boilerplate required to describe
152/// the type of an object chain.
153///
154/// # Example:
155///
156/// Instead of writing this...
157///
158/// ```rust
159/// # struct A;
160/// # struct B;
161/// # struct C;
162/// use object_chain::{Chain, Link};
163/// type ABC = Link<C, Link<B, Chain<A>>>;
164/// ```
165///
166/// ... the `chain!` macro allows you to write this:
167///
168/// ```rust
169/// # struct A;
170/// # struct B;
171/// # struct C;
172/// use object_chain::{Chain, Link, chain};
173/// type ABC = chain![A, B, C];
174/// ```
175///
176/// Note also how the order of types follows the type of objects in the chain instead of being
177/// reversed.
178#[macro_export(local_inner_macros)]
179macro_rules! chain {
180    [$($types:ty),+] => {
181        reverse!{ [ $($types),+ ] }
182    };
183}
184
185#[cfg(test)]
186#[allow(dead_code)]
187mod test {
188    use super::*;
189    use core::marker::PhantomData;
190
191    struct CompileTest {
192        chain1: chain![u8],
193        generic_in_chain: chain![Generic<'static, u32>],
194        chain: chain![u8, u16, u32],
195    }
196
197    struct Generic<'a, T> {
198        field: PhantomData<&'a T>,
199    }
200
201    #[test]
202    pub fn test() {
203        fn f(_obj_chain: &chain![u8, u16, u32]) {}
204
205        let test = CompileTest {
206            chain1: Chain::new(0),
207            generic_in_chain: Chain::new(Generic { field: PhantomData }),
208            chain: Chain::new(0u8).append(1u16).append(2u32),
209        };
210
211        f(&test.chain);
212
213        let chain = test.chain;
214        let (obj, chain) = chain.pop();
215        assert_eq!(obj, 2u32);
216
217        let (obj, chain) = chain.pop();
218        assert_eq!(obj, 1u16);
219
220        let (obj, chain) = chain.pop();
221        assert_eq!(obj, 0u8);
222        assert_eq!(chain, ());
223    }
224
225    #[test]
226    pub fn test_count() {
227        assert_eq!(1, Chain::new(0).len());
228        assert_eq!(3, Chain::new(0u8).append(1u16).append(2u32).len());
229    }
230
231    #[test]
232    pub fn test_accessing_elements_with_common_interface() {
233        // 1: First, we need to implement a common interface for all of our objects' types
234        trait AsU8 {
235            fn as_u8(&self) -> u8;
236        }
237
238        impl AsU8 for u8 {
239            fn as_u8(&self) -> u8 {
240                *self
241            }
242        }
243
244        impl AsU8 for u16 {
245            fn as_u8(&self) -> u8 {
246                *self as u8
247            }
248        }
249
250        // 2: Next, we need to implement an accessor interface
251        trait ReadableAsU8 {
252            fn read_from(&self, index: usize) -> &dyn AsU8;
253        }
254
255        impl<T: AsU8> ReadableAsU8 for Chain<T> {
256            fn read_from(&self, index: usize) -> &dyn AsU8 {
257                assert!(index == 0, "Out of bounds access!");
258                &self.object
259            }
260        }
261
262        impl<T: AsU8, CE: ChainElement<Inner = impl AsU8> + ReadableAsU8> ReadableAsU8 for Link<T, CE> {
263            fn read_from(&self, index: usize) -> &dyn AsU8 {
264                if index == self.len() - 1 {
265                    &self.object
266                } else {
267                    self.parent.read_from(index - 1)
268                }
269            }
270        }
271
272        // Through the accessor interface we can access any of our objects, but only through the
273        // common interface we defined in step 1.
274        fn do_test(obj_chain: &impl ReadableAsU8) {
275            assert_eq!(2, obj_chain.read_from(1).as_u8());
276        }
277
278        do_test(&Chain::new(1u8).append(2u16));
279    }
280}