#[cfg(feature = "async")]
use crate::config::BuilderGenerator;
use crate::{
context::{Resource, ResourceId},
error::TestError,
};
#[cfg(feature = "async")]
use core::future::Future;
#[cfg(feature = "async")]
use core::panic::AssertUnwindSafe;
use core::panic::{RefUnwindSafe, UnwindSafe};
#[cfg(feature = "async")] use paste::paste;
use std::result::Result;
pub struct HandlerParams {
#[cfg(feature = "async")]
pub(crate) runtime_builder: BuilderGenerator,
}
pub enum HandlerError {
NotInContext(ResourceId),
Panic(Box<dyn core::any::Any + Send>),
}
type TestResult = Result<(), Box<dyn TestError>>;
pub type HandlerResult = Result<TestResult, HandlerError>;
pub trait Handler<R, I, O, C, const ASYNC: bool> {
fn call(&mut self, context: C, params: &HandlerParams) -> HandlerResult;
fn get_resource_ids(&self) -> (Vec<ResourceId>, Vec<ResourceId>, Vec<ResourceId>);
}
macro_rules! int_implement_get_resource {
( $( $r:ident ),* ,0, $( $i:ident ),* ,1, $( $o:ident ),* ) => {
#[allow(unused_mut)]
fn get_resource_ids(&self) -> (Vec<ResourceId>, Vec<ResourceId>, Vec<ResourceId>) {
let reference = vec![$($r::get_resource_id()),*];
let inputs = vec![$($i::get_resource_id()),*];
let outputs = vec![$($o::get_resource_id()),*];
(reference, inputs, outputs)
}
}
}
macro_rules! implement_handler {
( $( $r:ident ),* ,0, $( $i:ident ),* ,1, $( $o:ident ),* ) => {
#[allow(non_snake_case, unused_parens)]
impl<F,$($r,)* $($i,)* $($o,)* C, E> Handler<($($r,)*), ($($i,)*), ($($o,)*), C, false> for F
where
F: Fn($(& $r,)* $($i),* ) -> Result<($($o),*) , E>,
F: RefUnwindSafe,
$(
$r: Resource<Context = C> + UnwindSafe,
$r: RefUnwindSafe,
)*
$(
$i: Resource<Context = C> + UnwindSafe,
)*
$(
$o: Resource<Context = C>,
)*
E: TestError + 'static,
{
fn call(&mut self, #[allow(unused_variables)] context: C, _params: &HandlerParams) -> HandlerResult {
$(
let $r = $r::from_context(&context).ok_or(HandlerError::NotInContext($r::get_resource_id()))?;
)*
$(
let $i = $i::from_context(&context).ok_or(HandlerError::NotInContext($i::get_resource_id()))?;
)*
let result = std::panic::catch_unwind(|| (self)($(& $r,)* $($i),*));
$(
$r::into_context(&context, $r);
)*
match result {
Ok(Ok(($($o),*))) => {
$(
$o::into_context(&context, $o);
)*
Ok(Ok(()))
}
Ok(Err(e)) => Ok(Err(Box::new(e))),
Err(panic_e) => {
Err(HandlerError::Panic(panic_e))
},
}
}
int_implement_get_resource!($($r),* ,0, $($i),* ,1, $($o),*);
}
#[cfg(feature = "async")]
paste!{
trait [<AsyncBorrowFn $($r)* $($i)* $($o)*>]<'a, $($r: ?Sized + 'a,)* $($i),*>: Fn($(&'a $r,)* $($i),*) -> Self::Fut {
type Out;
type Fut: Future<Output = Self::Out> + 'a;
}
impl<'a, $($r,)* $($i,)* F, Fut> [<AsyncBorrowFn $($r)* $($i)* $($o)*>]<'a, $($r,)* $($i),*> for F
where
$(
$r: ?Sized + 'a,
)*
F: Fn($(&'a $r,)* $($i),*) -> Fut,
Fut: Future + 'a,
{
type Out = Fut::Output;
type Fut = Fut;
}
#[allow(non_snake_case, unused_parens)]
impl<F,$($r,)* $($i,)* $($o,)* C, E> Handler<($($r,)*), ($($i,)*), ($($o,)*), C, true> for F
where
F: for<'a> [<AsyncBorrowFn $($r)* $($i)* $($o)*>] <'a, $($r,)* $($i,)* Out = Result<($($o),*), E>>,
F: RefUnwindSafe,
$(
$r: Resource<Context = C> + UnwindSafe,
$r: RefUnwindSafe,
)*
$(
$i: Resource<Context = C> + UnwindSafe,
)*
$(
$o: Resource<Context = C>,
)*
E: TestError + 'static,
{
fn call(&mut self, #[allow(unused_variables)] context: C, params: &HandlerParams) -> HandlerResult {
$(
let $r = $r::from_context(&context).ok_or(HandlerError::NotInContext($r::get_resource_id()))?;
let [<ref_ $r>] = & $r;
)*
$(
let $i = $i::from_context(&context).ok_or(HandlerError::NotInContext($i::get_resource_id()))?;
)*
use futures_util::future::FutureExt;
let rt = (params.runtime_builder)().build().unwrap();
let fut = (self)($([<ref_ $r>],)* $($i),*);
let result = rt.block_on(AssertUnwindSafe(fut).catch_unwind());
$(
$r::into_context(&context, $r);
)*
match result {
Ok(Ok(($($o),*))) => {
$(
$o::into_context(&context, $o);
)*
Ok(Ok(()))
}
Ok(Err(e)) => Ok(Err(Box::new(e))),
Err(panic_e) => {
Err(HandlerError::Panic(panic_e))
},
}
}
int_implement_get_resource!($($r),* ,0, $($i),* ,1, $($o),*);
}
}
};
}
implement_handler!(, 0,, 1,);
implement_handler!(, 0,, 1, O1);
implement_handler!(, 0,, 1, O1, O2);
implement_handler!(, 0,, 1, O1, O2, O3);
implement_handler!(, 0, I1, 1,);
implement_handler!(, 0, I1, 1, O1);
implement_handler!(, 0, I1, 1, O1, O2);
implement_handler!(, 0, I1, 1, O1, O2, O3);
implement_handler!(, 0, I1, 1, O1, O2, O3, O4);
implement_handler!(, 0, I1, I2, 1,);
implement_handler!(, 0, I1, I2, 1, O1);
implement_handler!(, 0, I1, I2, 1, O1, O2);
implement_handler!(, 0, I1, I2, 1, O1, O2, O3);
implement_handler!(, 0, I1, I2, 1, O1, O2, O3, O4);
implement_handler!(, 0, I1, I2, 1, O1, O2, O3, O4, O5);
implement_handler!(, 0, I1, I2, I3, 1,);
implement_handler!(, 0, I1, I2, I3, 1, O1);
implement_handler!(, 0, I1, I2, I3, 1, O1, O2);
implement_handler!(, 0, I1, I2, I3, 1, O1, O2, O3);
implement_handler!(, 0, I1, I2, I3, 1, O1, O2, O3, O4);
implement_handler!(, 0, I1, I2, I3, 1, O1, O2, O3, O4, O5);
implement_handler!(, 0, I1, I2, I3, 1, O1, O2, O3, O4, O5, O6);
implement_handler!(, 0, I1, I2, I3, I4, 1,);
implement_handler!(, 0, I1, I2, I3, I4, 1, O1);
implement_handler!(, 0, I1, I2, I3, I4, 1, O1, O2);
implement_handler!(, 0, I1, I2, I3, I4, 1, O1, O2, O3);
implement_handler!(, 0, I1, I2, I3, I4, 1, O1, O2, O3, O4);
implement_handler!(, 0, I1, I2, I3, I4, 1, O1, O2, O3, O4, O5);
implement_handler!(, 0, I1, I2, I3, I4, 1, O1, O2, O3, O4, O5, O6);
implement_handler!(, 0, I1, I2, I3, I4, I5, 1,);
implement_handler!(, 0, I1, I2, I3, I4, I5, 1, O1);
implement_handler!(, 0, I1, I2, I3, I4, I5, 1, O1, O2);
implement_handler!(, 0, I1, I2, I3, I4, I5, 1, O1, O2, O3);
implement_handler!(, 0, I1, I2, I3, I4, I5, 1, O1, O2, O3, O4);
implement_handler!(, 0, I1, I2, I3, I4, I5, 1, O1, O2, O3, O4, O5);
implement_handler!(, 0, I1, I2, I3, I4, I5, 1, O1, O2, O3, O4, O5, O6);
implement_handler!(R1, 0,, 1,);
implement_handler!(R1, 0,, 1, O1);
implement_handler!(R1, 0,, 1, O1, O2);
implement_handler!(R1, 0,, 1, O1, O2, O3);
implement_handler!(R1, 0, I1, 1,);
implement_handler!(R1, 0, I1, 1, O1);
implement_handler!(R1, 0, I1, 1, O1, O2);
implement_handler!(R1, 0, I1, 1, O1, O2, O3);
implement_handler!(R1, 0, I1, 1, O1, O2, O3, O4);
implement_handler!(R1, 0, I1, I2, 1,);
implement_handler!(R1, 0, I1, I2, 1, O1);
implement_handler!(R1, 0, I1, I2, 1, O1, O2);
implement_handler!(R1, 0, I1, I2, 1, O1, O2, O3);
implement_handler!(R1, 0, I1, I2, 1, O1, O2, O3, O4);
implement_handler!(R1, 0, I1, I2, 1, O1, O2, O3, O4, O5);
implement_handler!(R1, 0, I1, I2, I3, 1,);
implement_handler!(R1, 0, I1, I2, I3, 1, O1);
implement_handler!(R1, 0, I1, I2, I3, 1, O1, O2);
implement_handler!(R1, 0, I1, I2, I3, 1, O1, O2, O3);
implement_handler!(R1, 0, I1, I2, I3, 1, O1, O2, O3, O4);
implement_handler!(R1, 0, I1, I2, I3, 1, O1, O2, O3, O4, O5);
implement_handler!(R1, 0, I1, I2, I3, 1, O1, O2, O3, O4, O5, O6);
implement_handler!(R1, 0, I1, I2, I3, I4, 1,);
implement_handler!(R1, 0, I1, I2, I3, I4, 1, O1);
implement_handler!(R1, 0, I1, I2, I3, I4, 1, O1, O2);
implement_handler!(R1, 0, I1, I2, I3, I4, 1, O1, O2, O3);
implement_handler!(R1, 0, I1, I2, I3, I4, 1, O1, O2, O3, O4);
implement_handler!(R1, 0, I1, I2, I3, I4, 1, O1, O2, O3, O4, O5);
implement_handler!(R1, 0, I1, I2, I3, I4, 1, O1, O2, O3, O4, O5, O6);
implement_handler!(R1, R2, 0,, 1,);
implement_handler!(R1, R2, 0,, 1, O1);
implement_handler!(R1, R2, 0,, 1, O1, O2);
implement_handler!(R1, R2, 0,, 1, O1, O2, O3);
implement_handler!(R1, R2, 0, I1, 1,);
implement_handler!(R1, R2, 0, I1, 1, O1);
implement_handler!(R1, R2, 0, I1, 1, O1, O2);
implement_handler!(R1, R2, 0, I1, 1, O1, O2, O3);
implement_handler!(R1, R2, 0, I1, 1, O1, O2, O3, O4);
implement_handler!(R1, R2, 0, I1, I2, 1,);
implement_handler!(R1, R2, 0, I1, I2, 1, O1);
implement_handler!(R1, R2, 0, I1, I2, 1, O1, O2);
implement_handler!(R1, R2, 0, I1, I2, 1, O1, O2, O3);
implement_handler!(R1, R2, 0, I1, I2, 1, O1, O2, O3, O4);
implement_handler!(R1, R2, 0, I1, I2, 1, O1, O2, O3, O4, O5);
implement_handler!(R1, R2, 0, I1, I2, I3, 1,);
implement_handler!(R1, R2, 0, I1, I2, I3, 1, O1);
implement_handler!(R1, R2, 0, I1, I2, I3, 1, O1, O2);
implement_handler!(R1, R2, 0, I1, I2, I3, 1, O1, O2, O3);
implement_handler!(R1, R2, 0, I1, I2, I3, 1, O1, O2, O3, O4);
implement_handler!(R1, R2, 0, I1, I2, I3, 1, O1, O2, O3, O4, O5);
implement_handler!(R1, R2, 0, I1, I2, I3, 1, O1, O2, O3, O4, O5, O6);
pub fn call_handler<R, I, O, C, const ASYNC: bool>(
context: C,
handler: &mut dyn Handler<R, I, O, C, ASYNC>,
params: &HandlerParams,
) -> HandlerResult {
handler.call(context, params)
}
pub fn describe_handler<R, I, O, C, const ASYNC: bool>(
handler: &dyn Handler<R, I, O, C, ASYNC>,
) -> (Vec<ResourceId>, Vec<ResourceId>, Vec<ResourceId>) {
handler.get_resource_ids()
}