test_dsl/
verb.rs

1use std::any::Any;
2use std::marker::PhantomData;
3
4use crate::arguments::VerbArgument;
5use crate::error::TestErrorCase;
6
7pub trait TestVerb<H>: 'static {
8    fn run(&self, harness: &mut H, node: &kdl::KdlNode) -> Result<(), TestErrorCase>;
9    fn clone_box(&self) -> Box<dyn TestVerb<H>>;
10}
11
12impl<H: 'static> Clone for Box<dyn TestVerb<H>> {
13    fn clone(&self) -> Self {
14        let this: &dyn TestVerb<H> = &**self;
15        this.clone_box()
16    }
17}
18
19pub struct FunctionVerb<H> {
20    func: BoxedCallable<H>,
21    _pd: PhantomData<fn(H)>,
22}
23
24impl<H> Clone for FunctionVerb<H> {
25    fn clone(&self) -> Self {
26        Self {
27            func: self.func.clone(),
28            _pd: self._pd,
29        }
30    }
31}
32
33impl<H> FunctionVerb<H> {
34    pub fn new<F, T>(func: F) -> Self
35    where
36        F: CallableVerb<H, T>,
37    {
38        FunctionVerb {
39            func: BoxedCallable::new(func),
40            _pd: PhantomData,
41        }
42    }
43}
44
45struct BoxedCallable<H> {
46    callable: Box<dyn Any>,
47    call_fn: fn(&dyn Any, &mut H, &kdl::KdlNode) -> Result<(), TestErrorCase>,
48    clone_fn: fn(&dyn Any) -> Box<dyn Any>,
49}
50
51impl<H> Clone for BoxedCallable<H> {
52    fn clone(&self) -> Self {
53        BoxedCallable {
54            callable: (self.clone_fn)(&*self.callable),
55            call_fn: self.call_fn,
56            clone_fn: self.clone_fn,
57        }
58    }
59}
60
61impl<H> BoxedCallable<H> {
62    fn new<F, T>(callable: F) -> Self
63    where
64        F: CallableVerb<H, T>,
65    {
66        BoxedCallable {
67            callable: Box::new(callable),
68            call_fn: |this, harness, node| {
69                let this: &F = this.downcast_ref().unwrap();
70                this.call(harness, node)
71            },
72            clone_fn: |this| {
73                let this: &F = this.downcast_ref().unwrap();
74                Box::new(this.clone())
75            },
76        }
77    }
78
79    fn call(&self, harness: &mut H, node: &kdl::KdlNode) -> Result<(), TestErrorCase> {
80        (self.call_fn)(&*self.callable, harness, node)
81    }
82}
83
84pub trait CallableVerb<H, T>: Clone + 'static {
85    fn call(&self, harness: &mut H, node: &kdl::KdlNode) -> Result<(), TestErrorCase>;
86}
87
88impl<H, F> CallableVerb<H, ((),)> for F
89where
90    F: Fn(&mut H) -> miette::Result<()>,
91    F: Clone + 'static,
92{
93    fn call(&self, harness: &mut H, node: &kdl::KdlNode) -> Result<(), TestErrorCase> {
94        self(harness).map_err(|error| TestErrorCase::Error {
95            error,
96            label: node.span(),
97        })
98    }
99}
100
101#[rustfmt::skip]
102macro_rules! all_the_tuples {
103    ($name:ident) => {
104        $name!([], T1);
105        $name!([T1], T2);
106        $name!([T1, T2], T3);
107        $name!([T1, T2, T3], T4);
108        $name!([T1, T2, T3, T4], T5);
109        $name!([T1, T2, T3, T4, T5], T6);
110        $name!([T1, T2, T3, T4, T5, T6], T7);
111        $name!([T1, T2, T3, T4, T5, T6, T7], T8);
112        $name!([T1, T2, T3, T4, T5, T6, T7, T8], T9);
113        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9], T10);
114        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10], T11);
115        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11], T12);
116        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12], T13);
117        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13], T14);
118        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14], T15);
119        $name!([T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15], T16);
120    };
121}
122
123macro_rules! impl_callable {
124    (
125        [$($ty:ident),*], $last:ident
126    ) => {
127        #[allow(non_snake_case, unused_mut)]
128        impl<H, F, $($ty,)* $last> CallableVerb<H, ($($ty,)* $last,)> for F
129            where
130                F: Fn(&mut H, $($ty,)* $last,) -> miette::Result<()>,
131                F: Clone + 'static,
132                $( $ty: VerbArgument, )*
133                $last: VerbArgument,
134        {
135            fn call(&self, harness: &mut H, node: &kdl::KdlNode) -> Result<(), TestErrorCase> {
136                let mut args = node.iter();
137
138                let total_count = 1
139                    $(
140                        + {
141                            const _: () = {
142                                #[allow(unused)]
143                                let $ty = ();
144                            };
145                            1
146                        }
147
148                    )*;
149
150                let mut running_count = 1;
151
152                $(
153                    let arg = args.next().ok_or_else(|| TestErrorCase::MissingArgument {
154                        parent: node.span(),
155                        missing: format!("This verb takes {} arguments, you're missing the {}th argument.", total_count, running_count),
156                    })?;
157
158                    let $ty = <$ty as VerbArgument>::from_value(arg).ok_or_else(|| {
159                        TestErrorCase::WrongArgumentType {
160                            parent: node.name().span(),
161                            argument: arg.span(),
162                            expected: format!("This verb takes a '{}' as its argument here.", <$ty as VerbArgument>::TYPE_NAME),
163                        }
164                    })?;
165                    running_count += 1;
166                )*
167
168                let _ = running_count;
169
170                let arg = args.next().ok_or_else(|| TestErrorCase::MissingArgument {
171                    parent: node.span(),
172                    missing: format!("This verb takes {tc} arguments, you're missing the {tc}th argument.", tc = total_count),
173                })?;
174                let $last = <$last as VerbArgument>::from_value(arg).ok_or_else(|| {
175                    TestErrorCase::WrongArgumentType {
176                        parent: node.name().span(),
177                        argument: arg.span(),
178                        expected: format!("This verb takes a '{}' as its argument here.", <$last as VerbArgument>::TYPE_NAME),
179                    }
180                })?;
181
182                self(harness, $($ty,)* $last,).map_err(|error| TestErrorCase::Error {
183                    error,
184                    label: node.span()
185                })
186            }
187        }
188    };
189}
190
191all_the_tuples!(impl_callable);
192
193impl<H: 'static> TestVerb<H> for FunctionVerb<H> {
194    fn run(&self, harness: &mut H, node: &kdl::KdlNode) -> Result<(), TestErrorCase> {
195        let res = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
196            self.func.call(harness, node)
197        }));
198
199        match res {
200            Ok(res) => res,
201            Err(error) => {
202                let mut message = "Something went wrong".to_string();
203
204                let payload = error;
205
206                if let Some(msg) = payload.downcast_ref::<&str>() {
207                    message = msg.to_string();
208                }
209
210                if let Some(msg) = payload.downcast_ref::<String>() {
211                    message.clone_from(msg);
212                }
213
214                Err(TestErrorCase::Panic {
215                    error: miette::Error::msg(message),
216                    label: node.span(),
217                })
218            }
219        }
220    }
221
222    fn clone_box(&self) -> Box<dyn TestVerb<H>> {
223        Box::new(self.clone())
224    }
225}