macro_rules! define_fn_wrapper {
($name:ident<$($generics:ident),*>(Fn($($param_name:ident: $param_ty:ty),*) -> $return_ty:ty)) => {
pub(crate) struct $name<$($generics),*>(std::sync::Arc<dyn Fn($($param_ty),*) -> $return_ty + Send + Sync>);
impl<$($generics),*> $name<$($generics),*> {
pub(crate) fn new<F>(predicate: F) -> Self
where
F: Fn($($param_ty),*) -> $return_ty + Send + Sync + 'static,
{
Self(std::sync::Arc::new(predicate))
}
pub(crate) fn call(&self, $($param_name: $param_ty),*) -> $return_ty {
(self.0)($($param_name),*)
}
}
impl<$($generics),*> Clone for $name<$($generics),*> {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl<$($generics),*> std::fmt::Debug for $name<$($generics),*> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!($name)).finish()
}
}
};
($name:ident<$($generics:ident),*>(Fn($($param_name:ident: $param_ty:ty),*))) => {
crate::utils::define_fn_wrapper!($name<$($generics),*>(Fn($($param_name: $param_ty),*) -> ()));
};
($name:ident<$($generics:ident),*>(Fn($param1:ty, $param2:ty) -> $return_ty:ty)) => {
crate::utils::define_fn_wrapper!($name<$($generics),*>(Fn(arg1: $param1, arg2: $param2) -> $return_ty));
};
($name:ident<$($generics:ident),*>(Fn($param1:ty, $param2:ty))) => {
crate::utils::define_fn_wrapper!($name<$($generics),*>(Fn(arg1: $param1, arg2: $param2) -> ()));
};
($name:ident<$($generics:ident),*>(Fn($param1:ty) -> $return_ty:ty)) => {
crate::utils::define_fn_wrapper!($name<$($generics),*>(Fn(arg1: $param1) -> $return_ty));
};
($name:ident<$($generics:ident),*>(Fn($param1:ty))) => {
crate::utils::define_fn_wrapper!($name<$($generics),*>(Fn(arg1: $param1) -> ()));
};
($name:ident<$($generics:ident),*>(Fn() -> $return_ty:ty)) => {
$crate::utils::define_fn_wrapper!($name<$($generics),*>(Fn() -> $return_ty));
};
($name:ident<$($generics:ident),*>(Fn())) => {
$crate::utils::define_fn_wrapper!($name<$($generics),*>(Fn() -> ()));
};
($name:ident(Fn($($param_name:ident: $param_ty:ty),*) -> $return_ty:ty)) => {
pub(crate) struct $name(std::sync::Arc<dyn Fn($($param_ty),*) -> $return_ty + Send + Sync>);
impl $name {
pub(crate) fn new<F>(predicate: F) -> Self
where
F: Fn($($param_ty),*) -> $return_ty + Send + Sync + 'static,
{
Self(std::sync::Arc::new(predicate))
}
pub(crate) fn call(&self, $($param_name: $param_ty),*) -> $return_ty {
(self.0)($($param_name),*)
}
}
impl Clone for $name {
fn clone(&self) -> Self {
Self(self.0.clone())
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(stringify!($name)).finish()
}
}
};
($name:ident(Fn($($param_name:ident: $param_ty:ty),*))) => {
$crate::utils::define_fn_wrapper!($name(Fn($($param_name: $param_ty),*) -> ()));
};
($name:ident(Fn($param1:ty, $param2:ty) -> $return_ty:ty)) => {
$crate::utils::define_fn_wrapper!($name(Fn(arg1: $param1, arg2: $param2) -> $return_ty));
};
($name:ident(Fn($param1:ty, $param2:ty))) => {
$crate::define_fn_wrapper!($name(Fn(arg1: $param1, arg2: $param2) -> ()));
};
($name:ident(Fn($param1:ty) -> $return_ty:ty)) => {
$crate::utils::define_fn_wrapper!($name(Fn(arg1: $param1) -> $return_ty));
};
($name:ident(Fn($param1:ty))) => {
$crate::utils::define_fn_wrapper!($name(Fn(arg1: $param1) -> ()));
};
($name:ident(Fn() -> $return_ty:ty)) => {
$crate::utils::define_fn_wrapper!($name(Fn() -> $return_ty));
};
($name:ident(Fn())) => {
$crate::utils::define_fn_wrapper!($name(Fn() -> ()));
};
}
pub(crate) use define_fn_wrapper;
#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use std::fmt::Debug;
define_fn_wrapper!(InOut<In, Out>(Fn(&In) -> Out));
define_fn_wrapper!(NoGeneric(Fn(&str) -> String));
#[test]
fn static_assertions() {
static_assertions::assert_impl_all!(InOut<String, String>: Send, Sync, Debug, Clone);
}
#[test]
fn call_ok() {
let wrapper = InOut::new(|input: &String| input.clone());
let result = wrapper.call(&"Hello, World!".to_string());
assert_eq!(result, "Hello, World!".to_string());
let wrapper = wrapper;
let result = wrapper.call(&"Hello, World!".to_string());
assert_eq!(result, "Hello, World!".to_string());
}
#[test]
fn debug_ok() {
let wrapper = InOut::new(|input: &String| input.clone());
let debug_str = format!("{wrapper:?}");
assert_eq!(debug_str, "InOut");
}
#[test]
fn debug_non_generic_ok() {
let wrapper = NoGeneric::new(|input: &str| input.to_owned());
assert_eq!(wrapper.call("hello"), "hello");
let debug_str = format!("{wrapper:?}");
assert_eq!(debug_str, "NoGeneric");
}
}