fix(napi-derive-backend): do not unwrap Option value in object getter if the type of field is Option<T>
This commit is contained in:
parent
d58e488fa2
commit
6d4b4af36f
7 changed files with 55 additions and 4 deletions
|
@ -239,20 +239,40 @@ impl NapiStruct {
|
|||
for field in self.fields.iter() {
|
||||
let field_js_name = &field.js_name;
|
||||
let ty = &field.ty;
|
||||
|
||||
let is_optional_field = if let syn::Type::Path(syn::TypePath {
|
||||
path: syn::Path { segments, .. },
|
||||
..
|
||||
}) = &ty
|
||||
{
|
||||
if let Some(last_path) = segments.last() {
|
||||
last_path.ident.to_string() == "Option"
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
};
|
||||
match &field.name {
|
||||
syn::Member::Named(ident) => {
|
||||
field_destructions.push(quote! { #ident });
|
||||
obj_field_setters.push(quote! { obj.set(#field_js_name, #ident)?; });
|
||||
if is_optional_field {
|
||||
obj_field_getters.push(quote! { let #ident: #ty = obj.get(#field_js_name)?; });
|
||||
} else {
|
||||
obj_field_getters.push(quote! { let #ident: #ty = obj.get(#field_js_name)?.expect(&format!("Field {} should exist", #field_js_name)); });
|
||||
}
|
||||
}
|
||||
syn::Member::Unnamed(i) => {
|
||||
field_destructions.push(quote! { arg#i });
|
||||
obj_field_setters.push(quote! { obj.set(#field_js_name, arg#1)?; });
|
||||
if is_optional_field {
|
||||
obj_field_getters.push(quote! { let arg#i: #ty = obj.get(#field_js_name)?; });
|
||||
} else {
|
||||
obj_field_getters.push(quote! { let arg#i: #ty = obj.get(#field_js_name)?.expect(&format!("Field {} should exist", #field_js_name)); });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let destructed_fields = if self.is_tuple {
|
||||
quote! {
|
||||
|
|
|
@ -35,7 +35,7 @@ impl Object {
|
|||
|
||||
let ty = type_of!(self.0.env, ret)?;
|
||||
|
||||
Ok(if ty == ValueType::Undefined {
|
||||
Ok(if ty == ValueType::Undefined || ty == ValueType::Null {
|
||||
None
|
||||
} else {
|
||||
Some(V::from_napi_value(self.0.env, ret)?)
|
||||
|
|
|
@ -73,6 +73,11 @@ Generated by [AVA](https://avajs.dev).
|
|||
export function getGlobal(): typeof global␊
|
||||
export function getUndefined(): void␊
|
||||
export function getNull(): JsNull␊
|
||||
export interface AllOptionalObject {␊
|
||||
name?: string | undefined | null␊
|
||||
age?: number | undefined | null␊
|
||||
}␊
|
||||
export function receiveAllOptionalObject(obj?: AllOptionalObject | undefined | null): void␊
|
||||
export function asyncPlus100(p: Promise<number>): Promise<number>␊
|
||||
/** This is an interface for package.json */␊
|
||||
export interface PackageJson {␊
|
||||
|
|
Binary file not shown.
|
@ -59,6 +59,7 @@ import {
|
|||
convertU32Array,
|
||||
createExternalTypedArray,
|
||||
mutateTypedArray,
|
||||
receiveAllOptionalObject,
|
||||
} from '../'
|
||||
|
||||
test('export const', (t) => {
|
||||
|
@ -202,6 +203,11 @@ test('function ts type override', (t) => {
|
|||
t.deepEqual(tsRename({ foo: 1, bar: 2, baz: 2 }), ['foo', 'bar', 'baz'])
|
||||
})
|
||||
|
||||
test('option object', (t) => {
|
||||
t.notThrows(() => receiveAllOptionalObject())
|
||||
t.notThrows(() => receiveAllOptionalObject({}))
|
||||
})
|
||||
|
||||
test('serde-json', (t) => {
|
||||
const packageJson = readPackageJson()
|
||||
t.is(packageJson.name, 'napi-rs')
|
||||
|
|
5
examples/napi/index.d.ts
vendored
5
examples/napi/index.d.ts
vendored
|
@ -63,6 +63,11 @@ export function createObj(): object
|
|||
export function getGlobal(): typeof global
|
||||
export function getUndefined(): void
|
||||
export function getNull(): JsNull
|
||||
export interface AllOptionalObject {
|
||||
name?: string | undefined | null
|
||||
age?: number | undefined | null
|
||||
}
|
||||
export function receiveAllOptionalObject(obj?: AllOptionalObject | undefined | null): void
|
||||
export function asyncPlus100(p: Promise<number>): Promise<number>
|
||||
/** This is an interface for package.json */
|
||||
export interface PackageJson {
|
||||
|
|
|
@ -27,3 +27,18 @@ fn get_undefined(env: Env) -> Result<JsUndefined> {
|
|||
fn get_null(env: Env) -> Result<JsNull> {
|
||||
env.get_null()
|
||||
}
|
||||
|
||||
#[napi(object)]
|
||||
struct AllOptionalObject {
|
||||
pub name: Option<String>,
|
||||
pub age: Option<u32>,
|
||||
}
|
||||
|
||||
#[napi]
|
||||
fn receive_all_optional_object(obj: Option<AllOptionalObject>) -> Result<()> {
|
||||
if !obj.is_none() {
|
||||
assert!(obj.as_ref().unwrap().name.is_none());
|
||||
assert!(obj.as_ref().unwrap().age.is_none());
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue