orx_meta/queue/
builder.rs

1use crate::queue::{QueueSingle, StQueue};
2use core::marker::PhantomData;
3
4/// A type-safe builder for queues such that:
5///
6/// * `push` can only be called correct number of times with correct types,
7/// * `finish` can only be called when all elements to reach the `Target` type are pushed.
8///
9/// Further, since queues can represent any struct, `QueueBuilder` can be used as a generic builder for any struct or tuple.
10///
11/// # Example
12///
13/// In the following example, we want to build a queue of four elements of types `u32`, `bool`, `char` and `String` respectively.
14///
15/// For this, we can create a builder with `QueueBuilder::<MyQueue>::new()` where `MyQueue` is the target type to instantiate.
16///
17/// ```
18/// use orx_meta::queue::*;
19///
20/// type MyQueue = Queue<u32, Queue<bool, Queue<char, QueueSingle<String>>>>;
21///
22/// let instance = QueueBuilder::<MyQueue>::new()
23///     .push(42)
24///     .push(true)
25///     .push('x')
26///     .push("foo".to_string())
27///     .finish();
28/// assert_eq!(instance.as_tuple(), (&42, &true, &'x', &"foo".to_string()));
29/// ```
30///
31/// This provides a convenient way to build complex types without errors while getting compiler support on what to push next.
32/// However, it is not easy to hand-write the type alias for complex recursive queue types.
33/// Therefore, this builder pattern is most useful when used together with the [`queue_of`] macro.
34/// The above example could be re-written as follows with the `queue_of` macro.
35///
36/// [`queue_of`]: crate::queue_of
37///
38/// ```
39/// use orx_meta::queue::*;
40/// use orx_meta::queue_of;
41///
42/// type MyQueue = queue_of!(u32, bool, char, String);
43///
44/// let instance = QueueBuilder::<MyQueue>::new()
45///     .push(42)
46///     .push(true)
47///     .push('x')
48///     .push("foo".to_string())
49///     .finish();
50/// assert_eq!(instance.as_tuple(), (&42, &true, &'x', &"foo".to_string()));
51/// ```
52///
53/// ## Examples - Type Safety
54///
55/// Note that this builder pattern is type safe in the sense that neither of the following wrong implementations compiles.
56///
57/// Here the elements are pushed in the wrong order:
58///
59/// ```compile_fail
60/// use orx_meta::queue::*;
61/// use orx_meta::queue_of;
62///
63/// type MyQueue = queue_of!(u32, bool, char, String);
64///
65/// let instance = QueueBuilder::<MyQueue>::new()
66///     .push(true) // wrong order!
67///     .push(42)
68///     .push('x')
69///     .push("foo".to_string())
70///     .finish();
71/// assert_eq!(instance.as_tuple(), (&42, &true, &'x', &"foo".to_string()));
72/// ```
73///
74/// And here, not all elements are pushed:
75///
76/// ```compile_fail
77/// use orx_meta::queue::*;
78/// use orx_meta::queue_of;
79///
80/// type MyQueue = queue_of!(u32, bool, char, String);
81///
82/// let instance = QueueBuilder::<MyQueue>::new()
83///     .push(42)
84///     .push(true)
85///     .push('x')
86///     .finish(); // forgot to push the String
87/// assert_eq!(instance.as_tuple(), (&42, &true, &'x', &"foo".to_string()));
88/// ```
89pub struct QueueBuilder<Target>
90where
91    Target: StQueue,
92{
93    target: PhantomData<Target>,
94}
95
96impl<Target> Default for QueueBuilder<Target>
97where
98    Target: StQueue,
99{
100    fn default() -> Self {
101        Self::new()
102    }
103}
104
105impl<Target> QueueBuilder<Target>
106where
107    Target: StQueue,
108{
109    /// Creates a new empty builder for the `Target` type defined as the generic argument.
110    pub fn new() -> Self {
111        Self {
112            target: Default::default(),
113        }
114    }
115
116    /// Pushes the next `element` to build the target type.
117    pub fn push(
118        self,
119        element: Target::Front,
120    ) -> QueueBuilding<Target, Target::Back, QueueSingle<Target::Front>> {
121        QueueBuilding::new(QueueSingle::new(element))
122    }
123}
124
125pub struct QueueBuilding<Target, Remaining, Current>
126where
127    Target: StQueue,
128    Remaining: StQueue,
129    Current: StQueue,
130{
131    target: PhantomData<Target>,
132    remaining: PhantomData<Remaining>,
133    current: Current,
134}
135
136impl<Target, Remaining, Current> QueueBuilding<Target, Remaining, Current>
137where
138    Target: StQueue,
139    Remaining: StQueue,
140    Current: StQueue,
141{
142    fn new(current: Current) -> Self {
143        Self {
144            target: Default::default(),
145            remaining: Default::default(),
146            current,
147        }
148    }
149
150    /// Pushes the next `element` to build the target type.
151    pub fn push(
152        self,
153        element: Remaining::Front,
154    ) -> QueueBuilding<Target, Remaining::Back, Current::PushBack<Remaining::Front>> {
155        QueueBuilding::new(self.current.push(element))
156    }
157
158    /// Completes the builder and returns the built target type.
159    pub fn finish(self) -> Current
160    where
161        Target: StQueue<Front = Current::Front, Back = Current::Back>,
162    {
163        self.current
164    }
165}