use core::future::Future;
use core::ops::AsyncFn;
use core::pin::Pin;
use crate::__rt::alloc::{boxed::Box, vec::Vec};
use crate::JsValue;
use crate::encode::{
Anchored, ArgAbi, BinaryEncode, BorrowScope, CallScoped, EncodeTypeDef, TypeDef,
};
use crate::ipc::{DecodeError, DecodedData, EncodedData};
use wry_bindgen_core::{JsExportSpec, JsFunctionArg, JsFunctionSignature};
use super::{ReturnAbi, ReturnAsync, ReturnSync};
#[doc(hidden)]
pub trait CallExportArgs<S: BorrowScope> {
fn arg_types() -> Vec<TypeDef>;
}
fn export_args<Args, S>(arg_names: &'static [&'static str]) -> Vec<JsFunctionArg>
where
Args: CallExportArgs<S>,
S: BorrowScope,
{
let arg_types = Args::arg_types();
let hidden_args = arg_types.len().saturating_sub(arg_names.len());
arg_types
.into_iter()
.enumerate()
.map(|(i, ty)| JsFunctionArg {
name: if i < hidden_args {
""
} else {
arg_names[i - hidden_args]
},
ty,
})
.collect()
}
#[doc(hidden)]
pub trait CallExport<Args, S: BorrowScope>
where
Args: CallExportArgs<S>,
{
type ReturnMetadata: EncodeTypeDef;
#[allow(clippy::too_many_arguments)]
fn export_spec(
self,
name: &'static str,
namespace: &'static [&'static str],
arg_names: &'static [&'static str],
this: bool,
public: bool,
start: bool,
variadic: bool,
) -> JsExportSpec
where
Self: Sized + Copy + 'static,
{
let signature = JsFunctionSignature::new(
name,
namespace,
export_args::<Args, S>(arg_names),
TypeDef::of::<Self::ReturnMetadata>(),
this,
public,
start,
variadic,
);
let callable = self;
JsExportSpec::new(signature, move |decoder| {
Ok(Self::call(&callable, decoder)?)
})
}
fn call(&self, decoder: &mut DecodedData) -> Result<EncodedData, DecodeError>;
}
#[doc(hidden)]
pub trait CallExportAsync<Args, S: BorrowScope>
where
Args: CallExportArgs<S>,
{
type ReturnMetadata: EncodeTypeDef;
#[allow(clippy::too_many_arguments)]
fn export_spec<F, P>(
self,
resolve_async: F,
name: &'static str,
namespace: &'static [&'static str],
arg_names: &'static [&'static str],
this: bool,
public: bool,
start: bool,
variadic: bool,
) -> JsExportSpec
where
F: Fn(Pin<Box<dyn Future<Output = Result<JsValue, JsValue>> + 'static>>) -> P + 'static,
P: EncodeTypeDef + 'static,
for<'a> &'a P: BinaryEncode,
Self: Sized + Copy + 'static,
{
let return_type = TypeDef::of::<P>();
let signature = JsFunctionSignature::new(
name,
namespace,
export_args::<Args, S>(arg_names),
return_type,
this,
public,
start,
variadic,
);
let callable = self;
JsExportSpec::new(signature, move |decoder| {
let result = resolve_async(Self::call_async(callable, decoder)?);
let mut encoder = EncodedData::default();
<&P as BinaryEncode>::encode(&result, &mut encoder);
core::mem::forget(result);
Ok(encoder)
})
}
fn call_async(
self,
decoder: &mut DecodedData,
) -> Result<Pin<Box<dyn Future<Output = Result<JsValue, JsValue>> + 'static>>, DecodeError>;
}
macro_rules! impl_call_export {
( $( [$lt:lifetime $A:ident] )* ) => {
impl<S, $($A,)*> CallExportArgs<S> for ($($A,)*)
where
S: BorrowScope,
$( $A: ArgAbi<S>, )*
{
#[allow(non_snake_case)]
fn arg_types() -> Vec<TypeDef> {
#[allow(unused_mut)]
let mut types = Vec::new();
$(
types.push(TypeDef::of::<<$A as ArgAbi<S>>::Wire>());
)*
types
}
}
impl<F, S, R, $($A,)*> CallExport<($($A,)*), S> for F
where
S: BorrowScope,
R: ReturnSync,
$( $A: ArgAbi<S>, )*
F: for<$($lt),*> Fn($( <$A as ArgAbi<S>>::Projected<$lt> ),*) -> R,
{
type ReturnMetadata = <R as ReturnAbi<CallScoped>>::Wire;
#[allow(non_snake_case)]
fn call(&self, decoder: &mut DecodedData) -> Result<EncodedData, DecodeError> {
let _ = &decoder; $( let $A = <$A as ArgAbi<S>>::decode(decoder)?; )*
let mut encoder = EncodedData::default();
let projected_guards = impl_call_export!(@proj self encoder [$($A)*] []);
impl_call_export!(@wb encoder projected_guards [$($A)*]);
Ok(encoder)
}
}
impl<F, S, R, $($A,)*> CallExportAsync<($($A,)*), S> for F
where
S: BorrowScope,
R: ReturnAsync,
F: Copy + 'static,
$( $A: ArgAbi<S> + 'static, <$A as ArgAbi<S>>::Guard: 'static, )*
F: for<$($lt),*> AsyncFn($( <$A as ArgAbi<S>>::Projected<$lt> ),*) -> R,
{
type ReturnMetadata = <R as ReturnAbi<Anchored>>::Wire;
#[allow(non_snake_case)]
fn call_async(
self,
decoder: &mut DecodedData,
) -> Result<Pin<Box<dyn Future<Output = Result<JsValue, JsValue>> + 'static>>, DecodeError> {
let _ = &decoder; $( let $A = <$A as ArgAbi<S>>::decode(decoder)?; )*
Ok(Box::pin(async move {
impl_call_export!(@proj_async_top self [$($A)*]).await
}))
}
}
};
(@proj $f:ident $enc:ident [] [$($c:ident)*]) => {{
ReturnSync::return_abi($f($($c),*), &mut $enc);
}};
(@proj $f:ident $enc:ident [$A:ident $($rest:ident)*] [$($c:ident)*]) => {
<$A as ArgAbi<S>>::project($A, |$A| {
impl_call_export!(@proj $f $enc [$($rest)*] [$($c)* $A])
})
};
(@wb $enc:ident $pgs:ident []) => {{
let () = $pgs;
}};
(@wb $enc:ident $pgs:ident [$A:ident $($rest:ident)*]) => {{
let (__wry_inner, __wry_pg) = $pgs;
<$A as ArgAbi<S>>::write_back(__wry_pg, &mut $enc);
impl_call_export!(@wb $enc __wry_inner [$($rest)*]);
}};
(@proj_async_top $f:ident []) => {
async move { ReturnAsync::into_js_result($f().await) }
};
(@proj_async_top $f:ident [$A:ident $($rest:ident)*]) => {
<$A as ArgAbi<S>>::project_async($A, async move |$A| {
impl_call_export!(@proj_async_inner $f [$($rest)*] [$A])
})
};
(@proj_async_inner $f:ident [] [$($c:ident)*]) => {
ReturnAsync::into_js_result($f($($c),*).await)
};
(@proj_async_inner $f:ident [$A:ident $($rest:ident)*] [$($c:ident)*]) => {
<$A as ArgAbi<S>>::project_async($A, async move |$A| {
impl_call_export!(@proj_async_inner $f [$($rest)*] [$($c)* $A])
}).await
};
}
impl_call_export!();
impl_call_export!(['a0 A0]);
impl_call_export!(['a0 A0]['a1 A1]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]['a4 A4]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]['a4 A4]['a5 A5]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]['a4 A4]['a5 A5]['a6 A6]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]['a4 A4]['a5 A5]['a6 A6]['a7 A7]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]['a4 A4]['a5 A5]['a6 A6]['a7 A7]['a8 A8]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]['a4 A4]['a5 A5]['a6 A6]['a7 A7]['a8 A8]['a9 A9]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]['a4 A4]['a5 A5]['a6 A6]['a7 A7]['a8 A8]['a9 A9]['a10 A10]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]['a4 A4]['a5 A5]['a6 A6]['a7 A7]['a8 A8]['a9 A9]['a10 A10]['a11 A11]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]['a4 A4]['a5 A5]['a6 A6]['a7 A7]['a8 A8]['a9 A9]['a10 A10]['a11 A11]['a12 A12]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]['a4 A4]['a5 A5]['a6 A6]['a7 A7]['a8 A8]['a9 A9]['a10 A10]['a11 A11]['a12 A12]['a13 A13]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]['a4 A4]['a5 A5]['a6 A6]['a7 A7]['a8 A8]['a9 A9]['a10 A10]['a11 A11]['a12 A12]['a13 A13]['a14 A14]);
impl_call_export!(['a0 A0]['a1 A1]['a2 A2]['a3 A3]['a4 A4]['a5 A5]['a6 A6]['a7 A7]['a8 A8]['a9 A9]['a10 A10]['a11 A11]['a12 A12]['a13 A13]['a14 A14]['a15 A15]);