test_dsl/
condition.rs

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