From 90cc0a6abe9a6c371b188cf3a9987f61ee48a1f4 Mon Sep 17 00:00:00 2001 From: Hana Date: Wed, 8 Feb 2023 22:30:12 +0800 Subject: [PATCH] feat(napi): convert ToNapiValue tuple to variadic tsfn (#1475) * refactor: convert ToNapiValue tuple to variadic tsfn * chore: resolve conflicts * fix: typo * chore: use into instead of to * chore: syntax compat --- crates/backend/src/typegen.rs | 129 ++++++++++++++------ crates/backend/src/typegen/const.rs | 2 +- crates/backend/src/typegen/fn.rs | 20 +-- crates/backend/src/typegen/struct.rs | 10 +- crates/napi/src/threadsafe_function.rs | 67 +++++++++- examples/napi/__test__/typegen.spec.ts.md | 1 + examples/napi/__test__/typegen.spec.ts.snap | Bin 3771 -> 3795 bytes examples/napi/__test__/values.spec.ts | 15 +++ examples/napi/index.d.ts | 1 + examples/napi/src/threadsafe_function.rs | 10 ++ 10 files changed, 202 insertions(+), 53 deletions(-) diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index f7eeb174..2f0efdf7 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -218,6 +218,12 @@ fn is_ts_union_type(rust_ty: &str) -> bool { .unwrap_or(false) } +const TSFN_RUST_TY: &str = "ThreadsafeFunction"; + +fn should_convert_tuple_to_variadic(rust_ty: &str) -> bool { + rust_ty == TSFN_RUST_TY +} + fn is_ts_function_type_notation(ty: &Type) -> bool { match ty { Type::Path(syn::TypePath { qself: None, path }) => { @@ -235,12 +241,32 @@ fn is_ts_function_type_notation(ty: &Type) -> bool { } } -pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (String, bool) { +pub fn ty_to_ts_type( + ty: &Type, + is_return_ty: bool, + is_struct_field: bool, + convert_tuple_to_variadic: bool, +) -> (String, bool, bool) { match ty { - Type::Reference(r) => ty_to_ts_type(&r.elem, is_return_ty, is_struct_field), + Type::Reference(r) => ty_to_ts_type(&r.elem, is_return_ty, is_struct_field, false), Type::Tuple(tuple) => { if tuple.elems.is_empty() { - ("undefined".to_owned(), false) + ("undefined".to_owned(), false, false) + } else if convert_tuple_to_variadic { + let variadic = &tuple + .elems + .iter() + .enumerate() + .map(|(i, arg)| { + let (ts_type, is_optional, _) = ty_to_ts_type(arg, false, false, false); + r#fn::FnArg { + arg: format!("arg{}", i), + ts_type, + is_optional, + } + }) + .collect::(); + (format!("{}", variadic), false, true) } else { ( format!( @@ -248,11 +274,12 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S tuple .elems .iter() - .map(|elem| ty_to_ts_type(elem, false, false).0) + .map(|elem| ty_to_ts_type(elem, false, false, false).0) .collect::>() .join(", ") ), false, + false, ) } } @@ -267,14 +294,18 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S .args .iter() .filter_map(|arg| match arg { - syn::GenericArgument::Type(generic_ty) => { - Some(ty_to_ts_type(generic_ty, false, false)).map(|(mut ty, is_struct_field)| { - if is_ts_union_type && is_ts_function_type_notation(generic_ty) { - ty = format!("({})", ty); - } - (ty, is_struct_field) - }) - } + syn::GenericArgument::Type(generic_ty) => Some(ty_to_ts_type( + generic_ty, + false, + false, + should_convert_tuple_to_variadic(&rust_ty), + )) + .map(|(mut ty, is_optional, is_variadic)| { + if is_ts_union_type && is_ts_function_type_notation(generic_ty) { + ty = format!("({})", ty); + } + (ty, is_optional, is_variadic) + }), _ => None, }) .collect::>() @@ -285,7 +316,7 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S if rust_ty == "Result" && is_return_ty { ts_ty = Some(args.first().unwrap().to_owned()); } else if rust_ty == "Option" { - ts_ty = args.first().map(|(arg, _)| { + ts_ty = args.first().map(|(arg, _, _)| { ( if is_struct_field { arg.to_string() @@ -295,56 +326,78 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S format!("{} | undefined | null", arg) }, true, + false, ) }); } else if rust_ty == "AsyncTask" { ts_ty = r#struct::TASK_STRUCTS.with(|t| { - let (output_type, _) = args.first().unwrap().to_owned(); + let (output_type, _, _) = args.first().unwrap().to_owned(); if let Some(o) = t.borrow().get(&output_type) { - Some((format!("Promise<{}>", o), false)) + Some((format!("Promise<{}>", o), false, false)) } else { - Some(("Promise".to_owned(), false)) + Some(("Promise".to_owned(), false, false)) } }); } else if rust_ty == "Reference" || rust_ty == "WeakReference" { ts_ty = r#struct::TASK_STRUCTS.with(|t| { // Reference => T if let Some(arg) = args.first() { - let (output_type, _) = arg.to_owned(); + let (output_type, _, _) = arg.to_owned(); if let Some(o) = t.borrow().get(&output_type) { - Some((o.to_owned(), false)) + Some((o.to_owned(), false, false)) } else { - Some((output_type, false)) + Some((output_type, false, false)) } } else { // Not NAPI-RS `Reference` - Some((rust_ty, false)) + Some((rust_ty, false, false)) } }); } else if let Some(&(known_ty, _, _)) = KNOWN_TYPES.get(rust_ty.as_str()) { if known_ty.contains("{}") { ts_ty = Some(( - fill_ty(known_ty, args.into_iter().map(|(arg, _)| arg).collect()), + fill_ty(known_ty, args.into_iter().map(|(arg, _, _)| arg).collect()), + false, false, )); } else { - ts_ty = Some((known_ty.to_owned(), false)); + ts_ty = Some((known_ty.to_owned(), false, false)); } } else if let Some(t) = crate::typegen::r#struct::CLASS_STRUCTS .with(|c| c.borrow_mut().get(rust_ty.as_str()).cloned()) { - ts_ty = Some((t, false)); - } else if rust_ty == "ThreadsafeFunction" { + ts_ty = Some((t, false, false)); + } else if rust_ty == TSFN_RUST_TY { let fatal_tsfn = match args.get(1) { - Some((arg, _)) => arg == "Fatal", + Some((arg, _, _)) => arg == "Fatal", _ => false, }; - let first_arg = args.first().map(|(arg, _)| arg).unwrap(); + let (args, is_variadic) = args + .first() + .map(|(arg, _, is_variadic)| (arg, is_variadic)) + .unwrap(); ts_ty = if fatal_tsfn { - Some((format!("(value: {}) => any", first_arg), false)) + Some(( + { + if *is_variadic { + format!("({}) => any", args) + } else { + format!("(value: {}) => any", args) + } + }, + false, + false, + )) } else { Some(( - format!("(err: Error | null, value: {}) => any", first_arg), + { + if *is_variadic { + format!("(err: Error | null, {}) => any", args) + } else { + format!("(err: Error | null, value: {}) => any", args) + } + }, + false, false, )) }; @@ -354,19 +407,25 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool, is_struct_field: bool) -> (S aliases .borrow() .get(rust_ty.as_str()) - .map(|a| (a.to_owned(), false)) + .map(|a| (a.to_owned(), false, false)) }); - ts_ty = type_alias.or(Some((rust_ty, false))); + ts_ty = type_alias.or(Some((rust_ty, false, false))); } } - ts_ty.unwrap_or_else(|| ("any".to_owned(), false)) + ts_ty.unwrap_or_else(|| ("any".to_owned(), false, false)) } - Type::Group(g) => ty_to_ts_type(&g.elem, is_return_ty, is_struct_field), + Type::Group(g) => ty_to_ts_type(&g.elem, is_return_ty, is_struct_field, false), Type::Array(a) => { - let (element_type, is_optional) = ty_to_ts_type(&a.elem, is_return_ty, is_struct_field); - (format!("{}[]", element_type), is_optional) + let (element_type, is_optional, _) = + ty_to_ts_type(&a.elem, is_return_ty, is_struct_field, false); + (format!("{}[]", element_type), is_optional, false) } - _ => ("any".to_owned(), false), + Type::Paren(p) => { + let (element_type, is_optional, _) = + ty_to_ts_type(&p.elem, is_return_ty, is_struct_field, false); + (element_type, is_optional, false) + } + _ => ("any".to_owned(), false, false), } } diff --git a/crates/backend/src/typegen/const.rs b/crates/backend/src/typegen/const.rs index e926608d..e3933d4e 100644 --- a/crates/backend/src/typegen/const.rs +++ b/crates/backend/src/typegen/const.rs @@ -17,7 +17,7 @@ impl ToTypeDef for NapiConst { def: format!( "export const {}: {}", &self.js_name, - ty_to_ts_type(&self.type_name, false, false).0 + ty_to_ts_type(&self.type_name, false, false, false).0 ), js_mod: self.js_mod.to_owned(), js_doc: js_doc_from_comments(&self.comments), diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs index be135729..8832650c 100644 --- a/crates/backend/src/typegen/fn.rs +++ b/crates/backend/src/typegen/fn.rs @@ -6,13 +6,13 @@ use syn::{Pat, PathArguments, PathSegment}; use super::{ty_to_ts_type, ToTypeDef, TypeDef}; use crate::{js_doc_from_comments, CallbackArg, FnKind, NapiFn}; -struct FnArg { - arg: String, - ts_type: String, - is_optional: bool, +pub(crate) struct FnArg { + pub(crate) arg: String, + pub(crate) ts_type: String, + pub(crate) is_optional: bool, } -struct FnArgList { +pub(crate) struct FnArgList { this: Option, args: Vec, last_required: Option, @@ -110,7 +110,7 @@ fn gen_callback_type(callback: &CallbackArg) -> String { .iter() .enumerate() .map(|(i, arg)| { - let (ts_type, is_optional) = ty_to_ts_type(arg, false, false); + let (ts_type, is_optional, _) = ty_to_ts_type(arg, false, false, false); FnArg { arg: format!("arg{}", i), ts_type, @@ -119,7 +119,7 @@ fn gen_callback_type(callback: &CallbackArg) -> String { }) .collect::(), ret = match &callback.ret { - Some(ty) => ty_to_ts_type(ty, true, false).0, + Some(ty) => ty_to_ts_type(ty, true, false, false).0, None => "void".to_owned(), } ) @@ -153,7 +153,7 @@ impl NapiFn { }) = arguments { if let Some(syn::GenericArgument::Type(ty)) = angle_bracketed_args.first() { - let (ts_type, _) = ty_to_ts_type(ty, false, false); + let (ts_type, _, _) = ty_to_ts_type(ty, false, false, false); return Some(FnArg { arg: "this".to_owned(), ts_type, @@ -178,7 +178,7 @@ impl NapiFn { i.mutability = None; } - let (ts_type, is_optional) = ty_to_ts_type(&path.ty, false, false); + let (ts_type, is_optional, _) = ty_to_ts_type(&path.ty, false, false, false); let ts_type = arg.use_overridden_type_or(|| ts_type); let arg = path.pat.to_token_stream().to_string().to_case(Case::Camel); @@ -230,7 +230,7 @@ impl NapiFn { .unwrap_or_else(|| "".to_owned()), _ => { let ret = if let Some(ret) = &self.ret { - let (ts_type, _) = ty_to_ts_type(ret, true, false); + let (ts_type, _, _) = ty_to_ts_type(ret, true, false, false); if ts_type == "undefined" { "void".to_owned() } else if ts_type == "Self" { diff --git a/crates/backend/src/typegen/struct.rs b/crates/backend/src/typegen/struct.rs index b67c0211..971edfd2 100644 --- a/crates/backend/src/typegen/struct.rs +++ b/crates/backend/src/typegen/struct.rs @@ -38,19 +38,19 @@ impl ToTypeDef for NapiImpl { TASK_STRUCTS.with(|t| { t.borrow_mut().insert( self.name.to_string(), - ty_to_ts_type(output_type, false, true).0, + ty_to_ts_type(output_type, false, true, false).0, ); }); } if let Some(output_type) = &self.iterator_yield_type { let next_type = if let Some(ref ty) = self.iterator_next_type { - ty_to_ts_type(ty, false, false).0 + ty_to_ts_type(ty, false, false, false).0 } else { "void".to_owned() }; let return_type = if let Some(ref ty) = self.iterator_return_type { - ty_to_ts_type(ty, false, false).0 + ty_to_ts_type(ty, false, false, false).0 } else { "void".to_owned() }; @@ -60,7 +60,7 @@ impl ToTypeDef for NapiImpl { original_name: None, def: format!( "[Symbol.iterator](): Iterator<{}, {}, {}>", - ty_to_ts_type(output_type, false, true).0, + ty_to_ts_type(output_type, false, true, false).0, return_type, next_type, ), @@ -118,7 +118,7 @@ impl NapiStruct { field_str.push_str("readonly ") } - let (arg, is_optional) = ty_to_ts_type(&f.ty, false, true); + let (arg, is_optional, _) = ty_to_ts_type(&f.ty, false, true, false); let arg = f.ts_type.as_ref().map(|ty| ty.to_string()).unwrap_or(arg); let sep = if is_optional { "?" } else { "" }; diff --git a/crates/napi/src/threadsafe_function.rs b/crates/napi/src/threadsafe_function.rs index 931ea898..736b5c58 100644 --- a/crates/napi/src/threadsafe_function.rs +++ b/crates/napi/src/threadsafe_function.rs @@ -208,9 +208,72 @@ impl Clone for ThreadsafeFunction { } } -impl FromNapiValue for ThreadsafeFunction { +pub trait JsValuesTupleIntoVec { + fn into_vec(self, env: &Env) -> Result>; +} + +impl JsValuesTupleIntoVec for T { + fn into_vec(self, env: &Env) -> Result> { + Ok(vec![JsUnknown(crate::Value { + env: env.0, + value: unsafe { ::to_napi_value(env.0, self)? }, + value_type: crate::ValueType::Unknown, + })]) + } +} + +macro_rules! impl_js_value_tuple_to_vec { + ($($ident:ident),*) => { + impl<$($ident: ToNapiValue),*> JsValuesTupleIntoVec for ($($ident,)*) { + fn into_vec(self, env: &Env) -> Result> { + #[allow(non_snake_case)] + let ($($ident,)*) = self; + Ok(vec![$(JsUnknown($crate::Value { + env: env.0, + value: unsafe { <$ident as ToNapiValue>::to_napi_value(env.0, $ident)? }, + value_type: $crate::ValueType::Unknown, + })),*]) + } + } + }; +} + +impl_js_value_tuple_to_vec!(A); +impl_js_value_tuple_to_vec!(A, B); +impl_js_value_tuple_to_vec!(A, B, C); +impl_js_value_tuple_to_vec!(A, B, C, D); +impl_js_value_tuple_to_vec!(A, B, C, D, E); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L, M); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L, M, N); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W); +impl_js_value_tuple_to_vec!(A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X); +impl_js_value_tuple_to_vec!( + A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y +); +impl_js_value_tuple_to_vec!( + A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z +); + +impl FromNapiValue + for ThreadsafeFunction +{ unsafe fn from_napi_value(env: sys::napi_env, napi_val: sys::napi_value) -> Result { - Self::create(env, napi_val, 0, |ctx| Ok(vec![ctx.value])) + Self::create(env, napi_val, 0, |ctx| ctx.value.into_vec(&ctx.env)) } } diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index cf98e6a6..7c627270 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -209,6 +209,7 @@ Generated by [AVA](https://avajs.dev). export function tsfnAsyncCall(func: (...args: any[]) => any): Promise␊ export function acceptThreadsafeFunction(func: (err: Error | null, value: number) => any): void␊ export function acceptThreadsafeFunctionFatal(func: (value: number) => any): void␊ + export function acceptThreadsafeFunctionTupleArgs(func: (err: Error | null, arg0: number, arg1: boolean, arg2: string) => any): void␊ export function getBuffer(): Buffer␊ export function appendBuffer(buf: Buffer): Buffer␊ export function getEmptyBuffer(): Buffer␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 269788d655202f4d9e62ed36d4abc40729503fc2..c299f3468f63e1c2ffa082879277876c5438c8b2 100644 GIT binary patch delta 3709 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 b2S+PT#k+O-&C06?H+TLIC++mRn>PRefI=l; delta 3706 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 { }) }) +Napi4Test('accept ThreadsafeFunction tuple args', async (t) => { + await new Promise((resolve, reject) => { + acceptThreadsafeFunctionTupleArgs((err, num, bool, str) => { + if (err) { + return reject(err) + } + t.is(num, 1) + t.is(bool, false) + t.is(str, 'NAPI-RS') + resolve() + }) + }) +}) + Napi4Test('object only from js', (t) => { return new Promise((resolve, reject) => { receiveObjectOnlyFromJs({ diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index cea2101e..b76c0778 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -199,6 +199,7 @@ export function tsfnCallWithCallback(func: (...args: any[]) => any): void export function tsfnAsyncCall(func: (...args: any[]) => any): Promise export function acceptThreadsafeFunction(func: (err: Error | null, value: number) => any): void export function acceptThreadsafeFunctionFatal(func: (value: number) => any): void +export function acceptThreadsafeFunctionTupleArgs(func: (err: Error | null, arg0: number, arg1: boolean, arg2: string) => any): void export function getBuffer(): Buffer export function appendBuffer(buf: Buffer): Buffer export function getEmptyBuffer(): Buffer diff --git a/examples/napi/src/threadsafe_function.rs b/examples/napi/src/threadsafe_function.rs index fe88ab64..a6bfea0f 100644 --- a/examples/napi/src/threadsafe_function.rs +++ b/examples/napi/src/threadsafe_function.rs @@ -116,3 +116,13 @@ pub fn accept_threadsafe_function_fatal(func: ThreadsafeFunction) { + thread::spawn(move || { + func.call( + Ok((1, false, "NAPI-RS".into())), + ThreadsafeFunctionCallMode::NonBlocking, + ); + }); +}