specialize_call/
macros.rs

1/// A macro producing the invocations of the specified generic function with specific types, depending on the value available in runtime.
2/// 
3/// Examples:
4/// ```rust
5/// 
6/// struct A<const I: usize>();
7/// struct B<const I: usize>();
8/// struct C<const I: usize>();
9/// 
10/// #[derive(Debug)]
11/// enum Select<const I: usize> {
12///     A,
13///     B,
14///     C,
15/// }
16/// 
17/// use core::any::type_name as tn;
18/// 
19/// #[test]
20/// fn specialize_call_1() {
21///     // there is a function `do_it` that takes one runtime argument, and one type-parameter.
22///     fn do_it<T>(_arg: usize) -> &'static str {
23///         tn::<T>()
24///     }
25/// 
26///     assert_eq!(
27///         specialize_call!(
28///             // invoke `do_it::<?>`
29///             do_it, 
30///             // use `(1)` as the arguments list
31///             (1), 
32///             // choose the type-parameter depending on this value
33///             Select::<1>::A, 
34///             // the choice map
35///             [
36///                 // if the provided value is `Select::A`, then invoke `do_it::<A>`
37///                 (Select::<1>::A => A::<1>),
38///                 // if the provided value is `Select::B`, then invoke `do_it::<B>`
39///                 (Select::<1>::B => B::<1>),
40///             ]),
41///         Some(tn::<A<1>>())
42///     );
43/// 
44///     assert_eq!(
45///         specialize_call!(do_it, (2), Select::<1>::B, [
46///                 (Select::<1>::A => A::<1>),
47///                 (Select::<1>::B => B::<1>),
48///             ]),
49///         Some(tn::<B<1>>()),
50///     );
51/// 
52///     assert_eq!(
53///         specialize_call!(do_it, (3), Select::<1>::C, [
54///                 (Select::<1>::A => A::<1>),
55///                 (Select::<1>::B => B::<1>),
56///             ]),
57///         None::<&str>,
58///     );
59/// }
60/// ```
61#[macro_export]
62macro_rules! specialize_call {
63    (@single_mapping,
64        ( $($acc_p:pat),* ),
65        ( $($acc_t:ty),* ),
66        $func:tt, $args:tt, $select:tt,
67        [ ($p:pat => $($t:ty),+ ) $(, $p_t:tt)* $(,)* ],
68        $mappings:tt
69    ) => {
70        specialize_call!(@specialize_call,
71            ( $($acc_p,)* $p ),
72            ( $($acc_t,)* $($t),+ ),
73
74            $func, $args, $select,
75            $mappings
76        );
77
78        specialize_call!(@single_mapping,
79            ( $($acc_p),* ),
80            ( $($acc_t),* ),
81            $func, $args, $select,
82            [ $($p_t),* ],
83            $mappings
84        );
85    };
86    (@single_mapping,
87        $acc_p:tt, $acc_t: tt,
88        $func:tt, $args:tt, $select:tt,
89        [],
90        $mappings:tt
91    ) => {};
92
93    (@specialize_call,
94        $acc_p:tt,
95        $acc_t:tt,
96        $func:tt,
97        $args:tt,
98        $select:tt,
99
100        ($head:tt $(, $tail:tt)*)
101    ) => {
102        specialize_call!(@single_mapping,
103            $acc_p, $acc_t,
104            $func, $args, $select,
105            $head,
106            ( $($tail),* )
107        );
108    };
109    (@specialize_call,
110        $acc_p:tt,
111        $acc_t:tt,
112        $func:tt,
113        $args:tt,
114        $select:tt,
115
116        ()
117    ) => {
118        specialize_call!(@maybe_invoke,
119            $acc_p,
120            $acc_t,
121            $select,
122            $func,
123            $args
124        );
125    };
126
127    (@maybe_invoke,
128        ( $($acc_p:pat),* ),
129        ( $($acc_t:ty),* ),
130        $select:expr,
131        $func:ident,
132        ($($arg:expr),*)
133    ) => {
134        #[allow(unused_parens)]
135        if matches!($select, ( $($acc_p),* )) { break Some($func::<$($acc_t),*>( $($arg),* )); }
136    };
137
138    ($func:ident, ($($arg:expr),* $(,)*), $select:expr, $($mapping:tt),+ $(,)*) => {
139        loop {
140            specialize_call!(@specialize_call,
141                (), (),
142                $func,
143                ($($arg),*),
144                $select,
145                ( $($mapping),* )
146            );
147            break None
148        }
149    };
150}