feat(napi-derive): allow partial implement From/To Napivalue for Object (#1448)
This commit is contained in:
parent
e79eb34118
commit
c8352a1fb0
9 changed files with 99 additions and 27 deletions
|
@ -80,6 +80,8 @@ pub struct NapiStruct {
|
||||||
pub fields: Vec<NapiStructField>,
|
pub fields: Vec<NapiStructField>,
|
||||||
pub is_tuple: bool,
|
pub is_tuple: bool,
|
||||||
pub kind: NapiStructKind,
|
pub kind: NapiStructKind,
|
||||||
|
pub object_from_js: bool,
|
||||||
|
pub object_to_js: bool,
|
||||||
pub js_mod: Option<String>,
|
pub js_mod: Option<String>,
|
||||||
pub comments: Vec<String>,
|
pub comments: Vec<String>,
|
||||||
pub implement_iterator: bool,
|
pub implement_iterator: bool,
|
||||||
|
|
|
@ -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! {
|
quote! {
|
||||||
impl napi::bindgen_prelude::TypeName for #name {
|
impl napi::bindgen_prelude::TypeName for #name {
|
||||||
fn type_name() -> &'static str {
|
fn type_name() -> &'static str {
|
||||||
|
@ -569,33 +609,9 @@ impl NapiStruct {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl napi::bindgen_prelude::ToNapiValue for #name {
|
#to_napi_value
|
||||||
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;
|
#from_napi_value
|
||||||
#(#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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl napi::bindgen_prelude::ValidateNapiValue for #name {}
|
impl napi::bindgen_prelude::ValidateNapiValue for #name {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,8 @@ macro_rules! attrgen {
|
||||||
(strict, Strict(Span)),
|
(strict, Strict(Span)),
|
||||||
(return_if_invalid, ReturnIfInvalid(Span)),
|
(return_if_invalid, ReturnIfInvalid(Span)),
|
||||||
(object, Object(Span)),
|
(object, Object(Span)),
|
||||||
|
(object_from_js, ObjectFromJs(Span, Option<bool>)),
|
||||||
|
(object_to_js, ObjectToJs(Span, Option<bool>)),
|
||||||
(custom_finalize, CustomFinalize(Span)),
|
(custom_finalize, CustomFinalize(Span)),
|
||||||
(namespace, Namespace(Span, String, Span)),
|
(namespace, Namespace(Span, String, Span)),
|
||||||
(iterator, Iterator(Span)),
|
(iterator, Iterator(Span)),
|
||||||
|
|
|
@ -995,6 +995,8 @@ impl ConvertToAST for syn::ItemStruct {
|
||||||
fields,
|
fields,
|
||||||
is_tuple,
|
is_tuple,
|
||||||
kind: struct_kind,
|
kind: struct_kind,
|
||||||
|
object_from_js: opts.object_from_js(),
|
||||||
|
object_to_js: opts.object_to_js(),
|
||||||
js_mod: namespace,
|
js_mod: namespace,
|
||||||
comments: extract_doc_comments(&self.attrs),
|
comments: extract_doc_comments(&self.attrs),
|
||||||
implement_iterator,
|
implement_iterator,
|
||||||
|
|
|
@ -172,6 +172,11 @@ Generated by [AVA](https://avajs.dev).
|
||||||
}␊
|
}␊
|
||||||
export function createObjWithProperty(): { value: ArrayBuffer, get getter(): number }␊
|
export function createObjWithProperty(): { value: ArrayBuffer, get getter(): number }␊
|
||||||
export function getterFromObj(): 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>␊
|
export function asyncPlus100(p: Promise<number>): Promise<number>␊
|
||||||
/** This is an interface for package.json */␊
|
/** This is an interface for package.json */␊
|
||||||
export interface PackageJson {␊
|
export interface PackageJson {␊
|
||||||
|
|
Binary file not shown.
|
@ -89,6 +89,7 @@ import {
|
||||||
returnJsFunction,
|
returnJsFunction,
|
||||||
testSerdeRoundtrip,
|
testSerdeRoundtrip,
|
||||||
createObjWithProperty,
|
createObjWithProperty,
|
||||||
|
receiveObjectOnlyFromJs,
|
||||||
dateToNumber,
|
dateToNumber,
|
||||||
chronoDateToMillis,
|
chronoDateToMillis,
|
||||||
derefUint8Array,
|
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
|
const Napi5Test = Number(process.versions.napi) >= 5 ? test : test.skip
|
||||||
|
|
||||||
Napi5Test('Date test', (t) => {
|
Napi5Test('Date test', (t) => {
|
||||||
|
|
5
examples/napi/index.d.ts
vendored
5
examples/napi/index.d.ts
vendored
|
@ -162,6 +162,11 @@ export interface TsTypeChanged {
|
||||||
}
|
}
|
||||||
export function createObjWithProperty(): { value: ArrayBuffer, get getter(): number }
|
export function createObjWithProperty(): { value: ArrayBuffer, get getter(): number }
|
||||||
export function getterFromObj(): 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>
|
export function asyncPlus100(p: Promise<number>): Promise<number>
|
||||||
/** This is an interface for package.json */
|
/** This is an interface for package.json */
|
||||||
export interface PackageJson {
|
export interface PackageJson {
|
||||||
|
|
|
@ -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]
|
#[napi]
|
||||||
fn list_obj_keys(obj: Object) -> Vec<String> {
|
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 {
|
fn getter_from_obj() -> u32 {
|
||||||
42
|
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,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue