macro_rules! function_setter {
($interface_name:ident {
$(
$function_js_name:literal {
$setter_name:ident(
$(
$function_argument_name:ident: $function_argument_type:ty
),*
) -> $function_borrowed_return_type:ty;
// TODO: remove this hack and properly handle types
$function_owned_return_type:ty = $function_create_owned_return_type:expr;
}
)*
}) => {
impl<'env> $interface_name<'env> {
$(
pub fn $setter_name<F>(
&mut self,
env: &napi::Env,
function_callback: F,
) -> Result<(), napi::Error>
where
F: for<'function_context> Fn(
&'function_context napi::Env,
$($function_argument_type,)*
) -> Result<
Option<$function_borrowed_return_type>,
napi::Error,
>
+ std::panic::RefUnwindSafe
+ 'static,
{
let function_callback: bindgen_prelude::Function<
// Treat arguments as `Unknown` to make sure type errors are handled inside the closure
bindgen_prelude::Unknown,
// Extend the return type's lifetime beyond the lifetime of the closure
Option<$function_owned_return_type>,
> =
env.create_function_from_closure($function_js_name, move |function_context| {
let ($($function_argument_name),*) = function_context.args()?;
let function_return_value = match std::panic::catch_unwind(|| function_callback(function_context.env, $($function_argument_name),*)) {
Ok(function_result) => function_result?,
Err(panic_payload) => {
tracing::error!(
message = "Function panic",
event_handler = $function_js_name,
?panic_payload
);
return Err(napi::Error::from_reason("Function panic"));
}
};
$function_create_owned_return_type(function_return_value)
})?;
self.inner.set_named_property($function_js_name, function_callback)?;
Ok(())
}
)*
}
};
}
pub(crate) use function_setter;