feat(napi-derive): allow partial implement From/To Napivalue for Object (#1448)

This commit is contained in:
LongYinan 2023-01-24 14:51:16 +08:00 committed by GitHub
parent e79eb34118
commit c8352a1fb0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 99 additions and 27 deletions

View file

@ -80,6 +80,8 @@ pub struct NapiStruct {
pub fields: Vec<NapiStructField>,
pub is_tuple: bool,
pub kind: NapiStructKind,
pub object_from_js: bool,
pub object_to_js: bool,
pub js_mod: Option<String>,
pub comments: Vec<String>,
pub implement_iterator: bool,

View file

@ -558,6 +558,46 @@ impl NapiStruct {
}
};
let to_napi_value = if self.object_to_js {
quote! {
impl napi::bindgen_prelude::ToNapiValue for #name {
unsafe fn to_napi_value(env: napi::bindgen_prelude::sys::napi_env, val: #name) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
let env_wrapper = napi::bindgen_prelude::Env::from(env);
let mut obj = env_wrapper.create_object()?;
let #destructed_fields = val;
#(#obj_field_setters)*
napi::bindgen_prelude::Object::to_napi_value(env, obj)
}
}
}
} else {
quote! {}
};
let from_napi_value = if self.object_from_js {
quote! {
impl napi::bindgen_prelude::FromNapiValue for #name {
unsafe fn from_napi_value(
env: napi::bindgen_prelude::sys::napi_env,
napi_val: napi::bindgen_prelude::sys::napi_value
) -> napi::bindgen_prelude::Result<Self> {
let env_wrapper = napi::bindgen_prelude::Env::from(env);
let mut obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?;
#(#obj_field_getters)*
let val = #destructed_fields;
Ok(val)
}
}
}
} else {
quote! {}
};
quote! {
impl napi::bindgen_prelude::TypeName for #name {
fn type_name() -> &'static str {
@ -569,33 +609,9 @@ impl NapiStruct {
}
}
impl napi::bindgen_prelude::ToNapiValue for #name {
unsafe fn to_napi_value(env: napi::bindgen_prelude::sys::napi_env, val: #name) -> napi::bindgen_prelude::Result<napi::bindgen_prelude::sys::napi_value> {
let env_wrapper = napi::bindgen_prelude::Env::from(env);
let mut obj = env_wrapper.create_object()?;
#to_napi_value
let #destructed_fields = val;
#(#obj_field_setters)*
napi::bindgen_prelude::Object::to_napi_value(env, obj)
}
}
impl napi::bindgen_prelude::FromNapiValue for #name {
unsafe fn from_napi_value(
env: napi::bindgen_prelude::sys::napi_env,
napi_val: napi::bindgen_prelude::sys::napi_value
) -> napi::bindgen_prelude::Result<Self> {
let env_wrapper = napi::bindgen_prelude::Env::from(env);
let mut obj = napi::bindgen_prelude::Object::from_napi_value(env, napi_val)?;
#(#obj_field_getters)*
let val = #destructed_fields;
Ok(val)
}
}
#from_napi_value
impl napi::bindgen_prelude::ValidateNapiValue for #name {}
}

View file

@ -56,6 +56,8 @@ macro_rules! attrgen {
(strict, Strict(Span)),
(return_if_invalid, ReturnIfInvalid(Span)),
(object, Object(Span)),
(object_from_js, ObjectFromJs(Span, Option<bool>)),
(object_to_js, ObjectToJs(Span, Option<bool>)),
(custom_finalize, CustomFinalize(Span)),
(namespace, Namespace(Span, String, Span)),
(iterator, Iterator(Span)),

View file

@ -995,6 +995,8 @@ impl ConvertToAST for syn::ItemStruct {
fields,
is_tuple,
kind: struct_kind,
object_from_js: opts.object_from_js(),
object_to_js: opts.object_to_js(),
js_mod: namespace,
comments: extract_doc_comments(&self.attrs),
implement_iterator,

View file

@ -172,6 +172,11 @@ Generated by [AVA](https://avajs.dev).
}␊
export function createObjWithProperty(): { value: ArrayBuffer, get getter(): number }␊
export function getterFromObj(): number␊
export interface ObjectOnlyFromJs {␊
count: number␊
callback: (err: Error | null, value: number) => any␊
}␊
export function receiveObjectOnlyFromJs(obj: { count: number, callback: (err: Error | null, count: number) => void }): void␊
export function asyncPlus100(p: Promise<number>): Promise<number>
/** This is an interface for package.json */␊
export interface PackageJson {␊

View file

@ -89,6 +89,7 @@ import {
returnJsFunction,
testSerdeRoundtrip,
createObjWithProperty,
receiveObjectOnlyFromJs,
dateToNumber,
chronoDateToMillis,
derefUint8Array,
@ -816,6 +817,22 @@ Napi4Test('accept ThreadsafeFunction Fatal', async (t) => {
})
})
Napi4Test('object only from js', (t) => {
return new Promise((resolve, reject) => {
receiveObjectOnlyFromJs({
count: 100,
callback: (err: Error | null, count: number) => {
if (err) {
reject(err)
} else {
t.is(count, 100)
resolve()
}
},
})
})
})
const Napi5Test = Number(process.versions.napi) >= 5 ? test : test.skip
Napi5Test('Date test', (t) => {

View file

@ -162,6 +162,11 @@ export interface TsTypeChanged {
}
export function createObjWithProperty(): { value: ArrayBuffer, get getter(): number }
export function getterFromObj(): number
export interface ObjectOnlyFromJs {
count: number
callback: (err: Error | null, value: number) => any
}
export function receiveObjectOnlyFromJs(obj: { count: number, callback: (err: Error | null, count: number) => void }): void
export function asyncPlus100(p: Promise<number>): Promise<number>
/** This is an interface for package.json */
export interface PackageJson {

View file

@ -1,4 +1,7 @@
use napi::{bindgen_prelude::*, JsGlobal, JsNull, JsObject, JsUndefined, Property};
use napi::{
bindgen_prelude::*, threadsafe_function::ThreadsafeFunction, JsGlobal, JsNull, JsObject,
JsUndefined, Property,
};
#[napi]
fn list_obj_keys(obj: Object) -> Vec<String> {
@ -101,3 +104,23 @@ pub fn create_obj_with_property(env: Env) -> Result<JsObject> {
fn getter_from_obj() -> u32 {
42
}
#[napi(object, object_to_js = false)]
struct ObjectOnlyFromJs {
pub count: u32,
pub callback: ThreadsafeFunction<u32>,
}
#[napi]
fn receive_object_only_from_js(
#[napi(ts_arg_type = "{ count: number, callback: (err: Error | null, count: number) => void }")]
obj: ObjectOnlyFromJs,
) {
let ObjectOnlyFromJs { callback, count } = obj;
std::thread::spawn(move || {
callback.call(
Ok(count),
napi::threadsafe_function::ThreadsafeFunctionCallMode::NonBlocking,
);
});
}