1use std::any::Any;
4use std::marker::PhantomData;
5
6use crate::BoxedArguments;
7use crate::argument::ParseArguments;
8use crate::argument::VerbArgument;
9use crate::error::TestErrorCase;
10
11pub trait Condition<H>: std::fmt::Debug + Clone + 'static {
16 type Arguments: ParseArguments<H>;
18
19 fn check_now(&self, harness: &H, arguments: &Self::Arguments) -> miette::Result<bool>;
27
28 fn wait_until(&self, harness: &H, arguments: &Self::Arguments) -> miette::Result<bool>;
36}
37
38pub(crate) struct ErasedCondition<H> {
39 condition: Box<dyn Any>,
40 fn_parse_args:
41 fn(&crate::TestDsl<H>, &kdl::KdlNode) -> Result<Box<dyn BoxedArguments<H>>, TestErrorCase>,
42 fn_check_now: fn(&dyn Any, &H, &dyn Any) -> miette::Result<bool>,
43 fn_wait_util: fn(&dyn Any, &H, &dyn Any) -> miette::Result<bool>,
44 fn_clone: fn(&dyn Any) -> Box<dyn Any>,
45}
46
47impl<H> std::fmt::Debug for ErasedCondition<H> {
48 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
49 f.debug_struct("ErasedCondition")
50 .field("condition", &self.condition)
51 .field("fn_parse_args", &self.fn_parse_args)
52 .field("fn_check_now", &self.fn_check_now)
53 .field("fn_wait_util", &self.fn_wait_util)
54 .field("fn_clone", &self.fn_clone)
55 .finish()
56 }
57}
58
59impl<H> Clone for ErasedCondition<H> {
60 fn clone(&self) -> Self {
61 Self {
62 condition: (self.fn_clone)(&*self.condition),
63 fn_parse_args: self.fn_parse_args,
64 fn_check_now: self.fn_check_now,
65 fn_wait_util: self.fn_wait_util,
66 fn_clone: self.fn_clone,
67 }
68 }
69}
70
71impl<H> ErasedCondition<H> {
72 pub(crate) fn erase<C>(condition: C) -> Self
73 where
74 C: Condition<H>,
75 {
76 ErasedCondition {
77 condition: Box::new(condition),
78 fn_parse_args: |test_dsl, node| {
79 <C::Arguments as ParseArguments<H>>::parse(test_dsl, node).map(|a| {
80 let args = Box::new(a);
81 args as _
82 })
83 },
84 fn_check_now: |this, harness, arguments| {
85 let this: &C = this.downcast_ref().unwrap();
86 let arguments: &C::Arguments = arguments.downcast_ref().unwrap();
87
88 this.check_now(harness, arguments)
89 },
90 fn_wait_util: |this, harness, arguments| {
91 let this: &C = this.downcast_ref().unwrap();
92 let arguments: &C::Arguments = arguments.downcast_ref().unwrap();
93
94 this.wait_until(harness, arguments)
95 },
96 fn_clone: |this| {
97 let this: &C = this.downcast_ref().unwrap();
98
99 Box::new(this.clone())
100 },
101 }
102 }
103
104 pub(crate) fn parse_args(
105 &self,
106 test_dsl: &crate::TestDsl<H>,
107 node: &kdl::KdlNode,
108 ) -> Result<Box<dyn BoxedArguments<H>>, TestErrorCase> {
109 (self.fn_parse_args)(test_dsl, node)
110 }
111
112 pub(crate) fn check_now(&self, harness: &H, arguments: &dyn Any) -> miette::Result<bool> {
113 (self.fn_check_now)(&*self.condition, harness, arguments)
114 }
115}
116
117pub trait Checker<H, T>: Clone + 'static {
123 fn check(&self, harness: &H, arguments: &T) -> miette::Result<bool>;
125}
126
127struct BoxedChecker<H, T> {
128 checker: Box<dyn Any>,
129 check_fn: fn(&dyn Any, harness: &H, node: &T) -> miette::Result<bool>,
130 clone_fn: fn(&dyn Any) -> Box<dyn Any>,
131}
132
133impl<H, T> std::fmt::Debug for BoxedChecker<H, T> {
134 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135 f.debug_struct("BoxedChecker")
136 .field("checker", &self.checker)
137 .field("check_fn", &self.check_fn)
138 .field("clone_fn", &self.clone_fn)
139 .finish()
140 }
141}
142
143impl<H, T> BoxedChecker<H, T> {
144 fn new<C>(checker: C) -> Self
145 where
146 C: Checker<H, T>,
147 {
148 BoxedChecker {
149 checker: Box::new(checker),
150 check_fn: |this, harness, node| {
151 let this: &C = this.downcast_ref().unwrap();
152
153 this.check(harness, node)
154 },
155 clone_fn: |this| {
156 let this: &C = this.downcast_ref().unwrap();
157
158 Box::new(this.clone())
159 },
160 }
161 }
162
163 fn check(&self, harness: &H, node: &T) -> miette::Result<bool> {
164 (self.check_fn)(&*self.checker, harness, node)
165 }
166}
167
168impl<H, T> Clone for BoxedChecker<H, T> {
169 fn clone(&self) -> Self {
170 BoxedChecker {
171 checker: (self.clone_fn)(&*self.checker),
172 check_fn: self.check_fn,
173 clone_fn: self.clone_fn,
174 }
175 }
176}
177
178pub struct FunctionCondition<H, T> {
183 now: Option<BoxedChecker<H, T>>,
184 wait: Option<BoxedChecker<H, T>>,
185 _pd: PhantomData<fn(H)>,
186}
187
188impl<H, T> std::fmt::Debug for FunctionCondition<H, T> {
189 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
190 f.debug_struct("FunctionCondition")
191 .field("now", &self.now)
192 .field("wait", &self.wait)
193 .field("_pd", &self._pd)
194 .finish()
195 }
196}
197
198impl<H, T> FunctionCondition<H, T> {
199 pub fn new_now<C>(now: C) -> Self
203 where
204 C: Checker<H, T>,
205 {
206 FunctionCondition {
207 now: Some(BoxedChecker::new(now)),
208 wait: None,
209 _pd: PhantomData,
210 }
211 }
212
213 pub fn new_wait<C>(wait: C) -> Self
215 where
216 C: Checker<H, T>,
217 {
218 FunctionCondition {
219 now: None,
220 wait: Some(BoxedChecker::new(wait)),
221 _pd: PhantomData,
222 }
223 }
224
225 pub fn new_now_and_wait<C>(both: C) -> Self
227 where
228 C: Checker<H, T>,
229 {
230 FunctionCondition {
231 now: Some(BoxedChecker::new(both.clone())),
232 wait: Some(BoxedChecker::new(both)),
233 _pd: PhantomData,
234 }
235 }
236
237 pub fn with_now<C>(mut self, now: C) -> Self
239 where
240 C: Checker<H, T>,
241 {
242 self.now = Some(BoxedChecker::new(now));
243 self
244 }
245
246 pub fn with_wait<C>(mut self, wait: C) -> Self
248 where
249 C: Checker<H, T>,
250 {
251 self.wait = Some(BoxedChecker::new(wait));
252 self
253 }
254}
255
256impl<H, T> Clone for FunctionCondition<H, T> {
257 fn clone(&self) -> Self {
258 FunctionCondition {
259 now: self.now.clone(),
260 wait: self.wait.clone(),
261 _pd: PhantomData,
262 }
263 }
264}
265
266impl<H, F> Checker<H, ((),)> for F
267where
268 F: Fn(&H) -> miette::Result<bool>,
269 F: Clone + 'static,
270{
271 fn check(&self, harness: &H, _arguments: &((),)) -> miette::Result<bool> {
272 self(harness)
273 }
274}
275
276macro_rules! impl_callable {
277 (
278 [$($ty:ident),*], $last:ident
279 ) => {
280 #[allow(non_snake_case, unused_mut)]
281 impl<H, F, $($ty,)* $last> Checker<H, ($($ty,)* $last,)> for F
282 where
283 F: Fn(&H, $($ty,)* $last,) -> miette::Result<bool>,
284 F: Clone + 'static,
285 $( $ty: VerbArgument, )*
286 $last: VerbArgument,
287 {
288 fn check(&self, harness: &H, node: &($($ty,)* $last,)) -> miette::Result<bool> {
289 let ($($ty,)* $last,) = node.clone();
290 self(harness, $($ty,)* $last,)
291 }
292 }
293 };
294}
295
296all_the_tuples!(impl_callable);
297
298impl<H, T> Condition<H> for FunctionCondition<H, T>
299where
300 H: 'static,
301 T: ParseArguments<H>,
302{
303 type Arguments = T;
304 fn check_now(&self, harness: &H, arguments: &T) -> miette::Result<bool> {
305 let Some(check) = self.now.as_ref().map(|now| now.check(harness, arguments)) else {
306 return Err(TestErrorCase::InvalidCondition {
307 error: miette::miette!("FunctionCondition does not implement checking now"),
308 }
309 .into());
310 };
311
312 check
313 }
314
315 fn wait_until(&self, harness: &H, node: &T) -> miette::Result<bool> {
316 let Some(check) = self.wait.as_ref().map(|wait| wait.check(harness, node)) else {
317 return Err(TestErrorCase::InvalidCondition {
318 error: miette::miette!("FunctionCondition does not implement checking now"),
319 }
320 .into());
321 };
322
323 check
324 }
325}