dynamic_provider/
request_macro.rs

1#![allow(unused_parens)]
2
3/// Requests one of a set of possible values, refs, or tags from a provider.
4///
5/// If a value is found, its branch will be evaluated and none of the subsequent
6/// values will be requested from the provider.
7///
8/// ## Usage
9///
10/// ```
11/// use dynamic_provider::{define_tag, request, Lt, ProvideRef};
12///
13/// define_tag! {
14///     /// A tag that requires an arg
15///     tag TagWithArg: for<'x, 'y, 'arg> &'arg i32 => i32;
16///
17///     /// A tag that does not require an arg
18///     tag TagWithoutArg: for<'x, 'y, 'arg> i32;
19/// }
20///
21/// fn get_number<'x, 'y>(provider: &'x mut dyn for<'arg> ProvideRef<Lt!['y, 'arg]>) -> i32 {
22///     let arg_value = 1;
23///
24///     request! {
25///         from provider;
26///
27///         // This line is optional unless the lifetime list cannot be inferred:
28///         in 'x, 'y, '_;
29///
30///         // Request by tag with arg:
31///         tag x: TagWithArg where arg <- &arg_value => x,
32///
33///         // Request by tag without arg:
34///         tag x: TagWithoutArg => x,
35///
36///         // Request a 'static type:
37///         x: i32 => x,
38///
39///         // Request a unique reference:
40///         ref mut x: i32 => *x,
41///
42///         // Request a shared reference with an inferred type:
43///         ref x => *x,
44///
45///         // When none of the above requests were fulfilled:
46///         else => 0,
47///     }
48/// }
49/// ```
50///
51/// ## Notes
52///
53/// The `else` branch may also include a pattern that will be bound to the
54/// original provider like so:
55///
56/// ```ignore
57/// else provider => todo!()
58/// ```
59///
60/// If there is no `else` branch at all, this macro evaluates to a `Result`
61/// where `Ok` contains the output of one of the above branches, and `Err`
62/// contains the original provider.
63#[macro_export]
64macro_rules! request {
65    ($($token:tt)*) => {
66        $crate::__request_invoke! { $($token)* }
67    };
68}
69
70#[doc(hidden)]
71#[macro_export]
72macro_rules! __request_invoke {
73    (from $from:expr; in $($rest:tt)*) => {
74        $crate::__request_in! { @from = ($from); in $($rest)* }
75    };
76    (from $from:expr; $($rest:tt)*) => {
77        $crate::__request_body! {
78            @from = ($from);
79            @in = (_);
80            @tokens = { $($rest)* };
81        }
82    };
83}
84
85#[doc(hidden)]
86#[macro_export]
87macro_rules! __request_in {
88    (@from = $from:tt; in $($lt:lifetime),* $(,)?; $($rest:tt)* ) => {
89        $crate::__request_body! {
90            @from = $from;
91            @in = ($crate::Lt![$($lt),*]);
92            @tokens = { $($rest)* };
93        }
94    };
95    (@from = $from:tt; in $($lt:lifetime,)* .. $L:ty; $($rest:tt)* ) => {
96        $crate::__request_body! {
97            @from = $from;
98            @in = (Lt![$($lt,)* .. $L]);
99            @tokens = { $($rest)* };
100        }
101    };
102    (@from = $from:tt; in $L:ty; $($rest:tt)* ) => {
103        $crate::__request_body! {
104            @from = $from;
105            @in = ($L);
106            @tokens = { $($rest)* };
107        }
108    };
109}
110
111#[doc(hidden)]
112#[macro_export]
113macro_rules! __request_body {
114    {
115        @from = ($from:expr);
116        @in = ($L:ty);
117        @tokens = {
118            $($rest:tt)*
119        };
120    } => {
121        {
122            let mut _helper = $crate::__m::RequestHelper::<$L, _, _>::new($from);
123
124            $crate::__request_rules! {
125                @helper = _helper;
126                @tokens = { $($rest)* };
127            }
128        }
129    };
130}
131
132#[doc(hidden)]
133#[macro_export]
134macro_rules! __request_rules {
135    {
136        @helper = $helper:ident;
137        @tokens = {
138            else $pat:pat => $out:expr $(,)?
139        };
140    } => {
141        match $helper.finish() {
142            $crate::__m::Ok(_out) => _out,
143            $crate::__m::Err($pat) => $out,
144        }
145    };
146    {
147        @helper = $helper:ident;
148        @tokens = {
149            else => $out:expr $(,)?
150        };
151    } => {
152        match $helper.finish() {
153            $crate::__m::Ok(_out) => _out,
154            $crate::__m::Err(_) => $out,
155        }
156    };
157
158    {
159        @helper = $helper:ident;
160        @tokens = {
161            else $($rest:tt)*
162        };
163    } => {
164        $crate::__request_rules! {
165            @modifier = else;
166            @helper = $helper;
167            @tokens = { $($rest)* };
168        }
169    };
170
171    {
172        @helper = $helper:ident;
173        @tokens = {};
174    } => {
175        $helper.finish()
176    };
177
178    {
179        @helper = $helper:ident;
180        @tokens = {
181            ref mut $($rest:tt)*
182        };
183    } => {
184        $crate::__request_rules! {
185            @modifier = request_mut;
186            @helper = $helper;
187            @tokens = { $($rest)* };
188        }
189    };
190    {
191        @helper = $helper:ident;
192        @tokens = {
193            ref $($rest:tt)*
194        };
195    } => {
196        $crate::__request_rules! {
197            @modifier = request_ref;
198            @helper = $helper;
199            @tokens = { $($rest)* };
200        }
201    };
202    {
203        @helper = $helper:ident;
204        @tokens = {
205            tag $($rest:tt)*
206        };
207    } => {
208        $crate::__request_rules! {
209            @modifier = request;
210            @helper = $helper;
211            @tokens = { $($rest)* };
212        }
213    };
214    {
215        @helper = $helper:ident;
216        @tokens = {
217            $($rest:tt)*
218        };
219    } => {
220        $crate::__request_rules! {
221            @modifier = request_value;
222            @helper = $helper;
223            @tokens = { $($rest)* };
224        }
225    };
226
227    {
228        @modifier = $method:ident;
229        @helper = $helper:ident;
230        @tokens = {
231            $pat:tt: $Ty:ty
232            $(where arg <- $arg:expr)?
233            => $out:expr
234            $(, $($rest:tt)*)?
235        };
236    } => {
237        $helper = match $helper.$method::<$Ty>(($($arg)?)) {
238            $crate::__m::Ok($pat) => $crate::__m::RequestHelper::from_out($out),
239            $crate::__m::Err(_helper) => _helper,
240        };
241
242        $crate::__request_rules! {
243            @helper = $helper;
244            @tokens = { $($($rest)*)? };
245        }
246    };
247
248    {
249        @modifier = $modifier:tt;
250        @helper = $helper:ident;
251        @tokens = {
252            $pat:pat => $($rest:tt)*
253        };
254    } => {
255        $crate::__request_rules! {
256            @modifier = $modifier;
257            @helper = $helper;
258            @tokens = {
259                ($pat): _ => $($rest)*
260            };
261        }
262    };
263    {
264        @modifier = $modifier:tt;
265        @helper = $helper:ident;
266        @tokens = {
267            $($pat1:ident)* $(::$pat2:ident)* $({$($pat3:tt)*})? $(($($pat4:tt)*))?
268            : $Ty:ty
269            $(where $key:ident = $value:expr)?
270            => $($rest:tt)*
271        };
272    } => {
273        $crate::__request_rules! {
274            @modifier = $modifier;
275            @helper = $helper;
276            @tokens = {
277                ( $($pat1)* $(::$pat2)* $({ $($pat3)* })? $(( $($pat4)* ))? )
278                : $Ty
279                $(where $key = $value)?
280                => $($rest)*
281            };
282        }
283    };
284}
285
286#[cfg(test)]
287mod test {
288    use crate::{Lt, ProvideRef, QueryUsing};
289
290    #[test]
291    fn macro_usage() {
292        crate::define_tag! {
293            tag Foo: for<'x, 'y, 'input> &'input i32 => i32;
294        }
295        fn explicit_lts<'x, 'y>(
296            p: &'x mut dyn for<'i> crate::ProvideRef<crate::Lt!['y, 'i]>,
297        ) -> i32 {
298            let a = 0;
299            let b = 1;
300
301            request! {
302                from &mut *p;
303                in 'x, 'y, '_;
304                x: i32 => x,
305                tag x: Foo where arg <- &b => x + a,
306                ref mut x => *x,
307                ref y => *y,
308                else => 0,
309            }
310        }
311
312        fn implicit_lts(p: &mut dyn for<'i> crate::ProvideRef<crate::Lt!['_, 'i]>) -> i32 {
313            let a = 0;
314            let b = 1;
315
316            request! {
317                from &mut *p;
318                x: i32 => x,
319                tag x: Foo where arg <- &b => x + a,
320                ref mut x => *x,
321                ref y => *y,
322                else => 0,
323            }
324        }
325
326        fn check_both(p: &mut dyn for<'i> crate::ProvideRef<crate::Lt!['_, 'i]>) -> i32 {
327            let out1 = explicit_lts(p);
328            let out2 = implicit_lts(p);
329            assert_eq!(out1, out2);
330            out1
331        }
332
333        #[allow(clippy::type_complexity)]
334        struct CustomProvide(
335            i32,
336            for<'q, 'x, 'y, 'z> fn(
337                QueryUsing<'q, &'x mut i32, Lt!['x, 'y, 'z]>,
338            ) -> QueryUsing<'q, &'x mut i32, Lt!['x, 'y, 'z]>,
339        );
340
341        impl ProvideRef<Lt!['_, '_]> for CustomProvide {
342            fn provide_mut<'this>(&'this mut self, query: &mut crate::Query<Lt!['this, '_, '_]>) {
343                self.1(query.using(&mut self.0)).finish();
344            }
345        }
346
347        let mut p_value = CustomProvide(1_i32, |q| q.put_value(|x| *x));
348        assert_eq!(check_both(&mut p_value), 1);
349
350        let mut p_tag = CustomProvide(1_i32, |q| q.put_with_arg::<Foo>(|arg, x| *arg + *x));
351        assert_eq!(check_both(&mut p_tag), 2);
352
353        let mut p_ref = CustomProvide(3_i32, |q| q.put_ref(|x| x));
354        assert_eq!(check_both(&mut p_ref), 3);
355
356        let mut p_mut = CustomProvide(4_i32, |q| q.put_mut(|x| x));
357        assert_eq!(check_both(&mut p_mut), 4);
358
359        assert_eq!(check_both(&mut ()), 0);
360    }
361}