embedded_layout/
object_chain.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. This basic structure only allows you
5//! to query the number of elements, but you can implement a more useful trait for both `Link` and
6//! `Chain` to make this structure more useful.
7
8mod private {
9    pub trait Sealed {}
10
11    impl<V> Sealed for super::Chain<V> {}
12    impl<V, C: super::ChainElement> Sealed for super::Link<V, C> {}
13}
14
15/// A generic chain element
16pub trait ChainElement: Sized + private::Sealed {
17    /// Return the number of objects linked to this chain element
18    fn len(&self) -> usize;
19}
20
21/// This piece of the chain contains some object
22pub struct Link<V, C: ChainElement> {
23    /// The current object
24    pub object: V,
25
26    /// The rest of the object chain
27    pub parent: C,
28}
29
30impl<V, C: ChainElement> Link<V, C> {
31    /// Append an object to the chain
32    #[inline]
33    pub fn append<T>(self, item: T) -> Link<T, Self> {
34        Link {
35            object: item,
36            parent: self,
37        }
38    }
39}
40
41impl<V, C> Clone for Link<V, C>
42where
43    V: Clone,
44    C: ChainElement + Clone,
45{
46    fn clone(&self) -> Self {
47        Self {
48            object: self.object.clone(),
49            parent: self.parent.clone(),
50        }
51    }
52}
53
54impl<V, VC> ChainElement for Link<V, VC>
55where
56    VC: ChainElement,
57{
58    #[inline]
59    fn len(&self) -> usize {
60        self.parent.len() + 1
61    }
62}
63
64/// This piece marks the end of a chain
65pub struct Chain<V> {
66    /// The wrapped object.
67    pub object: V,
68}
69
70impl<V> Chain<V> {
71    /// Append an object to the chain
72    #[inline]
73    pub fn append<T>(self, item: T) -> Link<T, Self> {
74        Link {
75            object: item,
76            parent: self,
77        }
78    }
79}
80
81impl<V> Chain<V> {
82    /// Create a new [`Chain`] by wrapping the given object.
83    #[inline]
84    pub const fn new(object: V) -> Self {
85        Self { object }
86    }
87}
88
89impl<V> Clone for Chain<V>
90where
91    V: Clone,
92{
93    fn clone(&self) -> Self {
94        Self {
95            object: self.object.clone(),
96        }
97    }
98}
99
100impl<V> ChainElement for Chain<V> {
101    #[inline]
102    fn len(&self) -> usize {
103        1
104    }
105}
106
107/// Internal implementation of chain macro
108#[doc(hidden)]
109#[macro_export(local_inner_macros)]
110macro_rules! chain_impl {
111    ($x:ty) => {
112        Chain<$x>
113    };
114    ($x:ty,) => {
115        Chain<$x>
116    };
117    ($x:ty, $($rest:tt)+) => {
118        Link<$x, chain_impl! { $($rest)+ }>
119    };
120}
121
122/// Reverse the argument list to generate object chain
123#[doc(hidden)]
124#[macro_export(local_inner_macros)]
125macro_rules! reverse {
126    ([] $($reversed:tt)+) => {
127        chain_impl! { $($reversed)+ }
128    };
129    ([$first:ty] $($reversed:tt)*) => {
130        reverse! { [ ] $first, $($reversed)* }
131    };
132    ([$first:ty, $($rest:ty),*] $($reversed:tt)*) => {
133        reverse! { [ $($rest),* ] $first, $($reversed)* }
134    };
135}
136
137/// Creates an object chain from the argument types.
138///
139/// This macro reduces the boilerplate required to describe the type of an object chain.
140///
141/// # Example:
142///
143/// Instead of writing this...
144///
145/// ```rust
146/// use embedded_layout::prelude::*;
147/// use embedded_graphics::primitives::{Circle, Rectangle, Triangle};
148/// type Views = Link<Rectangle, Link<Circle, Chain<Triangle>>>;
149/// ```
150///
151/// ... the `chain!` macro allows you to write this:
152///
153/// ```rust
154/// use embedded_layout::prelude::*;
155/// use embedded_graphics::primitives::{Circle, Rectangle, Triangle};
156/// type Views = chain! { Triangle, Circle, Rectangle };
157/// ```
158///
159/// Note also how the order of types follows the type of objects in the chain instead of being
160/// reversed.
161#[macro_export(local_inner_macros)]
162macro_rules! chain {
163    ( $($types:ty),+ ) => {
164        reverse!{ [ $($types),+ ] }
165    };
166}
167
168#[cfg(test)]
169mod test {
170    #![allow(dead_code)]
171    use super::*;
172    use core::marker::PhantomData;
173
174    struct CompileTest {
175        chain1: chain! {
176            u8
177        },
178        generic_in_chain: chain! {
179            Generic<'static, u32>
180        },
181        chain: chain! {
182            u8, u16, u32
183        },
184    }
185
186    struct Generic<'a, T> {
187        field: PhantomData<&'a T>,
188    }
189
190    #[test]
191    pub fn test() {
192        fn f(_obj_chain: &chain! {u8, u16, u32}) {}
193
194        let test = CompileTest {
195            chain1: Chain::new(0),
196            generic_in_chain: Chain::new(Generic { field: PhantomData }),
197            chain: Chain::new(0u8).append(1u16).append(2u32),
198        };
199
200        f(&test.chain);
201    }
202
203    #[test]
204    pub fn test_count() {
205        assert_eq!(1, Chain::new(0).len());
206        assert_eq!(3, Chain::new(0u8).append(1u16).append(2u32).len());
207    }
208}
209
210#[cfg(test)]
211#[allow(dead_code)]
212mod test_macro {
213    use crate::prelude::*;
214    use embedded_graphics::primitives::{Rectangle, Triangle};
215
216    type Views = chain! {
217        Rectangle,
218        Rectangle,
219        Triangle
220    };
221}