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}