test_dsl/
macros.rs

1#[rustfmt::skip]
2macro_rules! all_the_tuples {
3    ($name:ident) => {
4        $name!([], T1);
5        $name!([T1], T2);
6        $name!([T1, T2], T3);
7        $name!([T1, T2, T3], T4);
8        $name!([T1, T2, T3, T4], T5);
9        $name!([T1, T2, T3, T4, T5], T6);
10        $name!([T1, T2, T3, T4, T5, T6], T7);
11        $name!([T1, T2, T3, T4, T5, T6, T7], T8);
12        $name!([T1, T2, T3, T4, T5, T6, T7, T8], T9);
13        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9], T10);
14        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T11);
15        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], T12);
16        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], T13);
17        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], T14);
18        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], T15);
19        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], T16);
20    };
21}
22
23/// Define a new type that implements [`ParseArguments`](crate::argument::ParseArguments)
24///
25/// This can then be used in your custom [`Verb`](crate::Verb) or [`Condition`](crate::condition::Condition) implementations.
26///
27/// **Note:** The definition uses `=` instead of the usual `:` to delimit fields and their types.
28/// This is on purpose, as this may later be expanded to allow for positional arguments as well.
29///
30/// ```
31/// use test_dsl::named_parameters;
32///
33/// named_parameters! {
34///     Frobnicator {
35///         foo = usize,
36///         name = String
37///     }
38/// }
39/// ```
40#[macro_export]
41macro_rules! named_parameters {
42    ( $vis:vis $param_name:ident { $($key:ident = $value:ty),* $(,)? }) => {
43        #[derive(Debug, Clone)]
44        $vis struct $param_name {
45            $($key: $value),*
46        }
47
48        impl<H> $crate::argument::ParseArguments<H> for $param_name {
49            fn parse(_: &$crate::TestDsl<H>, node: &$crate::kdl::KdlNode) -> Result<Self, $crate::error::TestErrorCase> {
50                $(
51                    let $key: $value = $crate::argument::VerbArgument::from_value(node.entry(stringify!($key)).unwrap()).unwrap();
52                )*
53
54                Ok($param_name {
55                    $(
56                        $key
57                    ),*
58                })
59            }
60        }
61    };
62}
63
64#[macro_export]
65#[cfg(not(doc))]
66#[expect(missing_docs, reason = "This is documented further below")]
67macro_rules! named_parameters_verb {
68    ($($input:tt)*) => {{
69        $crate::__inner_named_parameters_verb!(@impl { $($input)* })
70    }};
71}
72
73#[doc(hidden)]
74#[macro_export]
75macro_rules! __inner_named_parameters_verb {
76    (@impl { |$name:ident : &mut $ty:ty $(, $param_name:ident : $param_type:ty)* $(,)?| $rest:block }) => {{
77        #[derive(Debug, Clone)]
78        struct __NamedVerb {
79            $($param_name : $param_type),*
80        }
81
82        let verb = $crate::verb::FunctionVerb::<_, __NamedVerb>::new({
83            #[derive(Clone)]
84            struct __Caller;
85
86            impl $crate::verb::CallableVerb<$ty, __NamedVerb> for __Caller {
87                fn call(&self, harness: &mut $ty, node: &__NamedVerb) -> $crate::miette::Result<()> {
88
89                    let verb = |$name : &mut $ty $(, $param_name : $param_type)*,| {
90                        $rest
91                    };
92
93                    $(
94                        let $param_name: $param_type = node.$param_name.clone();
95                    )*
96
97                    verb(harness, $($param_name),*)
98                }
99            }
100
101            __Caller
102        });
103
104        impl<H> $crate::argument::ParseArguments<H> for __NamedVerb {
105            fn parse(_: &$crate::TestDsl<H>, node: &$crate::kdl::KdlNode) -> Result<Self, $crate::error::TestErrorCase> {
106                $(
107                    let $param_name: $param_type = $crate::argument::VerbArgument::from_value(node.entry(stringify!($param_name)).unwrap()).unwrap();
108                )*
109
110                Ok({
111                    __NamedVerb {
112                        $($param_name),*
113                    }
114                })
115            }
116        }
117
118        verb
119    }};
120}
121
122/// Define a verb using a closure, where the argument names are used as the key names
123///
124/// ```
125/// # use test_dsl::{TestDsl, named_parameters_verb};
126/// let mut dsl = TestDsl::<()>::new();
127///
128/// dsl.add_verb(
129///     "test",
130///     named_parameters_verb!(|_harness: &mut (), name: String, pi: usize| {
131///         println!("{name} = {pi}");
132///         Ok(())
133///     }),
134/// );
135/// ```
136#[cfg(doc)]
137#[macro_export]
138macro_rules! named_parameters_verb {
139    ($($input:tt)*) => {};
140}
141
142#[cfg(test)]
143mod tests {
144    use crate::TestDsl;
145    use crate::argument::ParseArguments;
146
147    #[test]
148    fn simple_kv() {
149        named_parameters!(CoolIntegers {
150            pi = usize,
151            name = String
152        });
153
154        let dsl = TestDsl::<()>::new();
155
156        let node = kdl::KdlNode::parse("foo pi=4 name=PI { other stuff }").unwrap();
157
158        let ints = CoolIntegers::parse(&dsl, &node).unwrap();
159
160        assert_eq!(ints.pi, 4);
161        assert_eq!(ints.name, "PI");
162    }
163
164    #[test]
165    fn simple_named_closure() {
166        let mut dsl = TestDsl::<()>::new();
167
168        dsl.add_verb(
169            "test",
170            named_parameters_verb!(|_harness: &mut (), name: String, pi: usize| {
171                println!("{name} = {pi}");
172                Ok(())
173            }),
174        );
175
176        dsl.add_verb(
177            "test_many",
178            named_parameters_verb!(|_harness: &mut (),
179                                    _name: String,
180                                    _pi: usize,
181                                    _pi1: usize,
182                                    _pi2: usize,
183                                    _pi3: usize,
184                                    _pi4: usize,
185                                    _pi5: usize,
186                                    _pi6: usize,
187                                    _pi7: usize,
188                                    _pi8: usize,
189                                    _pi9: usize,
190                                    _pi10: usize,
191                                    _pi11: usize,
192                                    _pi12: usize,
193                                    _pi13: usize,
194                                    _pi14: usize,
195                                    _pi15: usize,
196                                    _pi16: usize,
197                                    _pi17: usize,
198                                    _pi18: usize,
199                                    _pi19: usize,
200                                    _pi20: usize,
201                                    _pi21: usize,
202                                    _pi22: usize,
203                                    _pi23: usize,
204                                    _pi24: usize| { Ok(()) }),
205        );
206    }
207}