rtest/runner/
handler.rs

1#[cfg(feature = "async")]
2use crate::config::BuilderGenerator;
3use crate::{
4    context::{Resource, ResourceId},
5    error::TestError,
6};
7#[cfg(feature = "async")]
8use core::future::Future;
9#[cfg(feature = "async")]
10use core::panic::AssertUnwindSafe;
11use core::panic::{RefUnwindSafe, UnwindSafe};
12#[cfg(feature = "async")] use paste::paste;
13use std::result::Result;
14
15pub struct HandlerParams {
16    #[cfg(feature = "async")]
17    pub(crate) runtime_builder: BuilderGenerator,
18}
19
20pub enum HandlerError {
21    NotInContext(ResourceId),
22    Panic(Box<dyn core::any::Any + Send>),
23}
24
25// Ok -> Execution worked, contains Result of Test
26// Err, couldn't execute due to framework issues
27type TestResult = Result<(), Box<dyn TestError>>;
28pub type HandlerResult = Result<TestResult, HandlerError>;
29
30pub trait Handler<R, I, O, C, const ASYNC: bool> {
31    fn call(&mut self, context: C, params: &HandlerParams) -> HandlerResult;
32
33    /// (reference, input, output)
34    fn get_resource_ids(&self) -> (Vec<ResourceId>, Vec<ResourceId>, Vec<ResourceId>);
35}
36
37macro_rules! int_implement_get_resource {
38    ( $( $r:ident ),* ,0, $( $i:ident ),* ,1, $( $o:ident ),* ) => {
39                #[allow(unused_mut)]
40                fn get_resource_ids(&self) -> (Vec<ResourceId>, Vec<ResourceId>, Vec<ResourceId>) {
41                    let reference = vec![$($r::get_resource_id()),*];
42                    let inputs = vec![$($i::get_resource_id()),*];
43                    let outputs = vec![$($o::get_resource_id()),*];
44                    (reference, inputs, outputs)
45                }
46    }
47}
48
49macro_rules! implement_handler {
50    ( $( $r:ident ),* ,0, $( $i:ident ),* ,1, $( $o:ident ),* ) => {
51        #[allow(non_snake_case, unused_parens)]
52        impl<F,$($r,)* $($i,)* $($o,)* C, E> Handler<($($r,)*), ($($i,)*), ($($o,)*), C, false> for F
53            where
54                F: Fn($(& $r,)* $($i),* ) -> Result<($($o),*) , E>,
55                F: RefUnwindSafe,
56                $(
57                    $r: Resource<Context = C> + UnwindSafe,
58                    $r: RefUnwindSafe,
59                )*
60                $(
61                    $i:  Resource<Context = C> + UnwindSafe,
62                )*
63                $(
64                    $o:  Resource<Context = C>,
65                )*
66                E: TestError + 'static,
67            {
68                fn call(&mut self, #[allow(unused_variables)] context: C, _params: &HandlerParams) -> HandlerResult {
69                    $(
70                        let $r = $r::from_context(&context).ok_or(HandlerError::NotInContext($r::get_resource_id()))?;
71                    )*
72                    $(
73                        let $i = $i::from_context(&context).ok_or(HandlerError::NotInContext($i::get_resource_id()))?;
74                    )*
75                    let result = std::panic::catch_unwind(|| (self)($(& $r,)*  $($i),*));
76                    $(
77                        $r::into_context(&context, $r);
78                    )*
79                    match result {
80                        Ok(Ok(($($o),*))) => {
81                            $(
82                                $o::into_context(&context, $o);
83                            )*
84                            Ok(Ok(()))
85                        }
86                        Ok(Err(e)) => Ok(Err(Box::new(e))),
87                        Err(panic_e) => {
88                            Err(HandlerError::Panic(panic_e))
89                        },
90                    }
91                }
92
93                int_implement_get_resource!($($r),* ,0, $($i),* ,1, $($o),*);
94            }
95
96        #[cfg(feature = "async")]
97        paste!{
98
99        // https://github.com/rust-lang/rust/issues/113495#issuecomment-1627640952
100        // https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=504f2163e4836493588cc7da1b5f165e
101        trait [<AsyncBorrowFn $($r)* $($i)* $($o)*>]<'a, $($r: ?Sized + 'a,)* $($i),*>: Fn($(&'a $r,)* $($i),*) -> Self::Fut {
102            type Out;
103            type Fut: Future<Output = Self::Out> + 'a;
104        }
105
106        impl<'a, $($r,)* $($i,)* F, Fut> [<AsyncBorrowFn $($r)* $($i)* $($o)*>]<'a, $($r,)* $($i),*> for F
107            where
108                $(
109                    $r: ?Sized + 'a,
110                )*
111                F: Fn($(&'a $r,)* $($i),*) -> Fut,
112                Fut: Future + 'a,
113        {
114            type Out = Fut::Output;
115            type Fut = Fut;
116        }
117
118        #[allow(non_snake_case, unused_parens)]
119        impl<F,$($r,)* $($i,)* $($o,)* C, E> Handler<($($r,)*), ($($i,)*), ($($o,)*), C, true> for F
120            where
121                F: for<'a> [<AsyncBorrowFn $($r)* $($i)* $($o)*>] <'a, $($r,)* $($i,)* Out = Result<($($o),*), E>>,
122                F: RefUnwindSafe,
123                $(
124                    $r: Resource<Context = C> + UnwindSafe,
125                    $r: RefUnwindSafe,
126                )*
127                $(
128                    $i:  Resource<Context = C> + UnwindSafe,
129                )*
130                $(
131                    $o:  Resource<Context = C>,
132                )*
133                E: TestError + 'static,
134            {
135                fn call(&mut self, #[allow(unused_variables)] context: C, params: &HandlerParams) -> HandlerResult {
136                    $(
137                        let $r = $r::from_context(&context).ok_or(HandlerError::NotInContext($r::get_resource_id()))?;
138                        let [<ref_ $r>] = & $r;
139                    )*
140                    $(
141                        let $i = $i::from_context(&context).ok_or(HandlerError::NotInContext($i::get_resource_id()))?;
142                    )*
143                    use futures_util::future::FutureExt;
144                    let rt = (params.runtime_builder)().build().unwrap();
145                    let fut = (self)($([<ref_ $r>],)*  $($i),*);
146                    let result = rt.block_on(AssertUnwindSafe(fut).catch_unwind());
147                    $(
148                        $r::into_context(&context, $r);
149                    )*
150                    match result {
151                        Ok(Ok(($($o),*))) => {
152                            $(
153                                $o::into_context(&context, $o);
154                            )*
155                            Ok(Ok(()))
156                        }
157                        Ok(Err(e)) => Ok(Err(Box::new(e))),
158                        Err(panic_e) => {
159                            Err(HandlerError::Panic(panic_e))
160                        },
161                    }
162                }
163
164                int_implement_get_resource!($($r),* ,0, $($i),* ,1, $($o),*);
165            }
166        }
167
168    };
169}
170
171// 0 is just the delimiter between REFERENCE and INPUT
172// 1 is just the delimiter between INPUT and OUTPUT
173implement_handler!(, 0,, 1,);
174implement_handler!(, 0,, 1, O1);
175implement_handler!(, 0,, 1, O1, O2);
176implement_handler!(, 0,, 1, O1, O2, O3);
177implement_handler!(, 0, I1, 1,);
178implement_handler!(, 0, I1, 1, O1);
179implement_handler!(, 0, I1, 1, O1, O2);
180implement_handler!(, 0, I1, 1, O1, O2, O3);
181implement_handler!(, 0, I1, 1, O1, O2, O3, O4);
182implement_handler!(, 0, I1, I2, 1,);
183implement_handler!(, 0, I1, I2, 1, O1);
184implement_handler!(, 0, I1, I2, 1, O1, O2);
185implement_handler!(, 0, I1, I2, 1, O1, O2, O3);
186implement_handler!(, 0, I1, I2, 1, O1, O2, O3, O4);
187implement_handler!(, 0, I1, I2, 1, O1, O2, O3, O4, O5);
188implement_handler!(, 0, I1, I2, I3, 1,);
189implement_handler!(, 0, I1, I2, I3, 1, O1);
190implement_handler!(, 0, I1, I2, I3, 1, O1, O2);
191implement_handler!(, 0, I1, I2, I3, 1, O1, O2, O3);
192implement_handler!(, 0, I1, I2, I3, 1, O1, O2, O3, O4);
193implement_handler!(, 0, I1, I2, I3, 1, O1, O2, O3, O4, O5);
194implement_handler!(, 0, I1, I2, I3, 1, O1, O2, O3, O4, O5, O6);
195implement_handler!(, 0, I1, I2, I3, I4, 1,);
196implement_handler!(, 0, I1, I2, I3, I4, 1, O1);
197implement_handler!(, 0, I1, I2, I3, I4, 1, O1, O2);
198implement_handler!(, 0, I1, I2, I3, I4, 1, O1, O2, O3);
199implement_handler!(, 0, I1, I2, I3, I4, 1, O1, O2, O3, O4);
200implement_handler!(, 0, I1, I2, I3, I4, 1, O1, O2, O3, O4, O5);
201implement_handler!(, 0, I1, I2, I3, I4, 1, O1, O2, O3, O4, O5, O6);
202implement_handler!(, 0, I1, I2, I3, I4, I5, 1,);
203implement_handler!(, 0, I1, I2, I3, I4, I5, 1, O1);
204implement_handler!(, 0, I1, I2, I3, I4, I5, 1, O1, O2);
205implement_handler!(, 0, I1, I2, I3, I4, I5, 1, O1, O2, O3);
206implement_handler!(, 0, I1, I2, I3, I4, I5, 1, O1, O2, O3, O4);
207implement_handler!(, 0, I1, I2, I3, I4, I5, 1, O1, O2, O3, O4, O5);
208implement_handler!(, 0, I1, I2, I3, I4, I5, 1, O1, O2, O3, O4, O5, O6);
209implement_handler!(R1, 0,, 1,);
210implement_handler!(R1, 0,, 1, O1);
211implement_handler!(R1, 0,, 1, O1, O2);
212implement_handler!(R1, 0,, 1, O1, O2, O3);
213implement_handler!(R1, 0, I1, 1,);
214implement_handler!(R1, 0, I1, 1, O1);
215implement_handler!(R1, 0, I1, 1, O1, O2);
216implement_handler!(R1, 0, I1, 1, O1, O2, O3);
217implement_handler!(R1, 0, I1, 1, O1, O2, O3, O4);
218implement_handler!(R1, 0, I1, I2, 1,);
219implement_handler!(R1, 0, I1, I2, 1, O1);
220implement_handler!(R1, 0, I1, I2, 1, O1, O2);
221implement_handler!(R1, 0, I1, I2, 1, O1, O2, O3);
222implement_handler!(R1, 0, I1, I2, 1, O1, O2, O3, O4);
223implement_handler!(R1, 0, I1, I2, 1, O1, O2, O3, O4, O5);
224implement_handler!(R1, 0, I1, I2, I3, 1,);
225implement_handler!(R1, 0, I1, I2, I3, 1, O1);
226implement_handler!(R1, 0, I1, I2, I3, 1, O1, O2);
227implement_handler!(R1, 0, I1, I2, I3, 1, O1, O2, O3);
228implement_handler!(R1, 0, I1, I2, I3, 1, O1, O2, O3, O4);
229implement_handler!(R1, 0, I1, I2, I3, 1, O1, O2, O3, O4, O5);
230implement_handler!(R1, 0, I1, I2, I3, 1, O1, O2, O3, O4, O5, O6);
231implement_handler!(R1, 0, I1, I2, I3, I4, 1,);
232implement_handler!(R1, 0, I1, I2, I3, I4, 1, O1);
233implement_handler!(R1, 0, I1, I2, I3, I4, 1, O1, O2);
234implement_handler!(R1, 0, I1, I2, I3, I4, 1, O1, O2, O3);
235implement_handler!(R1, 0, I1, I2, I3, I4, 1, O1, O2, O3, O4);
236implement_handler!(R1, 0, I1, I2, I3, I4, 1, O1, O2, O3, O4, O5);
237implement_handler!(R1, 0, I1, I2, I3, I4, 1, O1, O2, O3, O4, O5, O6);
238implement_handler!(R1, R2, 0,, 1,);
239implement_handler!(R1, R2, 0,, 1, O1);
240implement_handler!(R1, R2, 0,, 1, O1, O2);
241implement_handler!(R1, R2, 0,, 1, O1, O2, O3);
242implement_handler!(R1, R2, 0, I1, 1,);
243implement_handler!(R1, R2, 0, I1, 1, O1);
244implement_handler!(R1, R2, 0, I1, 1, O1, O2);
245implement_handler!(R1, R2, 0, I1, 1, O1, O2, O3);
246implement_handler!(R1, R2, 0, I1, 1, O1, O2, O3, O4);
247implement_handler!(R1, R2, 0, I1, I2, 1,);
248implement_handler!(R1, R2, 0, I1, I2, 1, O1);
249implement_handler!(R1, R2, 0, I1, I2, 1, O1, O2);
250implement_handler!(R1, R2, 0, I1, I2, 1, O1, O2, O3);
251implement_handler!(R1, R2, 0, I1, I2, 1, O1, O2, O3, O4);
252implement_handler!(R1, R2, 0, I1, I2, 1, O1, O2, O3, O4, O5);
253implement_handler!(R1, R2, 0, I1, I2, I3, 1,);
254implement_handler!(R1, R2, 0, I1, I2, I3, 1, O1);
255implement_handler!(R1, R2, 0, I1, I2, I3, 1, O1, O2);
256implement_handler!(R1, R2, 0, I1, I2, I3, 1, O1, O2, O3);
257implement_handler!(R1, R2, 0, I1, I2, I3, 1, O1, O2, O3, O4);
258implement_handler!(R1, R2, 0, I1, I2, I3, 1, O1, O2, O3, O4, O5);
259implement_handler!(R1, R2, 0, I1, I2, I3, 1, O1, O2, O3, O4, O5, O6);
260
261pub fn call_handler<R, I, O, C, const ASYNC: bool>(
262    context: C,
263    handler: &mut dyn Handler<R, I, O, C, ASYNC>,
264    params: &HandlerParams,
265) -> HandlerResult {
266    handler.call(context, params)
267}
268
269pub fn describe_handler<R, I, O, C, const ASYNC: bool>(
270    handler: &dyn Handler<R, I, O, C, ASYNC>,
271) -> (Vec<ResourceId>, Vec<ResourceId>, Vec<ResourceId>) {
272    handler.get_resource_ids()
273}