Merge pull request #902 from napi-rs/beta-fix-1

Beta fix 1
This commit is contained in:
LongYinan 2021-12-02 17:04:06 +08:00 committed by GitHub
commit 8b77bc62ab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 132 additions and 13 deletions

View file

@ -383,6 +383,9 @@ export class ExternalObject<T> {
dts += indentLines(`}`, nest) + '\n' dts += indentLines(`}`, nest) + '\n'
break break
case 'enum': case 'enum':
if (!nested) {
idents.push(def.name)
}
dts += dts +=
indentLines(`${def.js_doc}export enum ${def.name} {`, nest) + '\n' indentLines(`${def.js_doc}export enum ${def.name} {`, nest) + '\n'
dts += indentLines(def.def, nest + 2) + '\n' dts += indentLines(def.def, nest + 2) + '\n'

View file

@ -239,17 +239,37 @@ impl NapiStruct {
for field in self.fields.iter() { for field in self.fields.iter() {
let field_js_name = &field.js_name; let field_js_name = &field.js_name;
let ty = &field.ty; 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 == "Option"
} else {
false
}
} else {
false
};
match &field.name { match &field.name {
syn::Member::Named(ident) => { syn::Member::Named(ident) => {
field_destructions.push(quote! { #ident }); field_destructions.push(quote! { #ident });
obj_field_setters.push(quote! { obj.set(#field_js_name, #ident)?; }); obj_field_setters.push(quote! { obj.set(#field_js_name, #ident)?; });
obj_field_getters.push(quote! { let #ident: #ty = obj.get(#field_js_name)?.expect(&format!("Field {} should exist", #field_js_name)); }); 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) => { syn::Member::Unnamed(i) => {
field_destructions.push(quote! { arg#i }); field_destructions.push(quote! { arg#i });
obj_field_setters.push(quote! { obj.set(#field_js_name, arg#1)?; }); obj_field_setters.push(quote! { obj.set(#field_js_name, arg#1)?; });
obj_field_getters.push(quote! { let arg#i: #ty = obj.get(#field_js_name)?.expect(&format!("Field {} should exist", #field_js_name)); }); 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)); });
}
} }
} }
} }

View file

@ -3,7 +3,7 @@ mod r#enum;
mod r#fn; mod r#fn;
pub(crate) mod r#struct; pub(crate) mod r#struct;
use std::collections::HashMap; use std::{cell::RefCell, collections::HashMap};
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use syn::Type; use syn::Type;
@ -17,6 +17,16 @@ pub struct TypeDef {
pub js_doc: String, pub js_doc: String,
} }
thread_local! {
static ALIAS: RefCell<HashMap<String, String>> = Default::default();
}
fn add_alias(name: String, alias: String) {
ALIAS.with(|aliases| {
aliases.borrow_mut().insert(name, alias);
});
}
pub fn js_doc_from_comments(comments: &[String]) -> String { pub fn js_doc_from_comments(comments: &[String]) -> String {
if comments.is_empty() { if comments.is_empty() {
return "".to_owned(); return "".to_owned();
@ -248,7 +258,13 @@ pub fn ty_to_ts_type(ty: &Type, is_return_ty: bool) -> (String, bool) {
)); ));
} else { } else {
// there should be runtime registered type in else // there should be runtime registered type in else
ts_ty = Some((rust_ty, false)); let type_alias = ALIAS.with(|aliases| {
aliases
.borrow()
.get(rust_ty.as_str())
.map(|a| (a.to_owned(), false))
});
ts_ty = type_alias.or(Some((rust_ty, false)));
} }
} }

View file

@ -1,9 +1,10 @@
use super::{ToTypeDef, TypeDef}; use super::{ToTypeDef, TypeDef};
use crate::{js_doc_from_comments, ty_to_ts_type, NapiConst}; use crate::{js_doc_from_comments, ty_to_ts_type, typegen::add_alias, NapiConst};
impl ToTypeDef for NapiConst { impl ToTypeDef for NapiConst {
fn to_type_def(&self) -> TypeDef { fn to_type_def(&self) -> TypeDef {
add_alias(self.name.to_string(), self.js_name.to_string());
TypeDef { TypeDef {
kind: "const".to_owned(), kind: "const".to_owned(),
name: self.js_name.to_owned(), name: self.js_name.to_owned(),

View file

@ -1,8 +1,9 @@
use super::{ToTypeDef, TypeDef}; use super::{add_alias, ToTypeDef, TypeDef};
use crate::{js_doc_from_comments, NapiEnum}; use crate::{js_doc_from_comments, NapiEnum};
impl ToTypeDef for NapiEnum { impl ToTypeDef for NapiEnum {
fn to_type_def(&self) -> TypeDef { fn to_type_def(&self) -> TypeDef {
add_alias(self.name.to_string(), self.js_name.to_string());
TypeDef { TypeDef {
kind: "enum".to_owned(), kind: "enum".to_owned(),
name: self.js_name.to_owned(), name: self.js_name.to_owned(),

View file

@ -1,7 +1,7 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::HashMap;
use super::{ToTypeDef, TypeDef}; use super::{add_alias, ToTypeDef, TypeDef};
use crate::{js_doc_from_comments, ty_to_ts_type, NapiImpl, NapiStruct, NapiStructKind}; use crate::{js_doc_from_comments, ty_to_ts_type, NapiImpl, NapiStruct, NapiStructKind};
thread_local! { thread_local! {
@ -15,6 +15,7 @@ impl ToTypeDef for NapiStruct {
c.borrow_mut() c.borrow_mut()
.insert(self.name.to_string(), self.js_name.clone()); .insert(self.name.to_string(), self.js_name.clone());
}); });
add_alias(self.name.to_string(), self.js_name.to_string());
TypeDef { TypeDef {
kind: String::from(if self.kind == NapiStructKind::Object { kind: String::from(if self.kind == NapiStructKind::Object {
"interface" "interface"

View file

@ -1,9 +1,8 @@
use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use napi_derive_backend::{bail_span, BindgenResult, Diagnostic}; use napi_derive_backend::{bail_span, BindgenResult, Diagnostic};
use proc_macro2::{Delimiter, Ident, Span, TokenTree}; use proc_macro2::{Delimiter, Ident, Span, TokenTree};
use std::{
cell::{Cell, RefCell},
collections::HashMap,
};
use syn::spanned::Spanned; use syn::spanned::Spanned;
thread_local! { thread_local! {

View file

@ -35,7 +35,7 @@ impl Object {
let ty = type_of!(self.0.env, ret)?; let ty = type_of!(self.0.env, ret)?;
Ok(if ty == ValueType::Undefined { Ok(if ty == ValueType::Undefined || ty == ValueType::Null {
None None
} else { } else {
Some(V::from_napi_value(self.0.env, ret)?) Some(V::from_napi_value(self.0.env, ret)?)

View file

@ -73,6 +73,20 @@ Generated by [AVA](https://avajs.dev).
export function getGlobal(): typeof global␊ export function getGlobal(): typeof global␊
export function getUndefined(): void␊ export function getUndefined(): void␊
export function getNull(): JsNull␊ export function getNull(): JsNull␊
export interface AllOptionalObject {␊
name?: string | undefined | null␊
age?: number | undefined | null␊
}␊
export function receiveAllOptionalObject(obj?: AllOptionalObject | undefined | null): void␊
export enum ALIAS {␊
A = 0,␊
B = 1␊
}␊
export interface AliasedStruct {␊
a: ALIAS␊
b: number␊
}␊
export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): 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 {␊

View file

@ -59,6 +59,10 @@ import {
convertU32Array, convertU32Array,
createExternalTypedArray, createExternalTypedArray,
mutateTypedArray, mutateTypedArray,
receiveAllOptionalObject,
fnReceivedAliased,
ALIAS,
AliasedStruct,
} from '../' } from '../'
test('export const', (t) => { test('export const', (t) => {
@ -202,6 +206,20 @@ test('function ts type override', (t) => {
t.deepEqual(tsRename({ foo: 1, bar: 2, baz: 2 }), ['foo', 'bar', 'baz']) t.deepEqual(tsRename({ foo: 1, bar: 2, baz: 2 }), ['foo', 'bar', 'baz'])
}) })
test('option object', (t) => {
t.notThrows(() => receiveAllOptionalObject())
t.notThrows(() => receiveAllOptionalObject({}))
})
test('aliased rust struct and enum', (t) => {
const a: ALIAS = ALIAS.A
const b: AliasedStruct = {
a,
b: 1,
}
t.notThrows(() => fnReceivedAliased(b, ALIAS.B))
})
test('serde-json', (t) => { test('serde-json', (t) => {
const packageJson = readPackageJson() const packageJson = readPackageJson()
t.is(packageJson.name, 'napi-rs') t.is(packageJson.name, 'napi-rs')

View file

@ -63,6 +63,20 @@ export function createObj(): object
export function getGlobal(): typeof global export function getGlobal(): typeof global
export function getUndefined(): void export function getUndefined(): void
export function getNull(): JsNull export function getNull(): JsNull
export interface AllOptionalObject {
name?: string | undefined | null
age?: number | undefined | null
}
export function receiveAllOptionalObject(obj?: AllOptionalObject | undefined | null): void
export enum ALIAS {
A = 0,
B = 1
}
export interface AliasedStruct {
a: ALIAS
b: number
}
export function fnReceivedAliased(s: AliasedStruct, e: ALIAS): 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 {

View file

@ -27,3 +27,35 @@ fn get_undefined(env: Env) -> Result<JsUndefined> {
fn get_null(env: Env) -> Result<JsNull> { fn get_null(env: Env) -> Result<JsNull> {
env.get_null() 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_some() {
assert!(obj.as_ref().unwrap().name.is_none());
assert!(obj.as_ref().unwrap().age.is_none());
}
Ok(())
}
#[napi(js_name = "ALIAS")]
pub enum AliasedEnum {
A,
B,
}
#[napi(object, js_name = "AliasedStruct")]
pub struct StructContainsAliasedEnum {
pub a: AliasedEnum,
pub b: u32,
}
#[napi]
fn fn_received_aliased(mut s: StructContainsAliasedEnum, e: AliasedEnum) {
s.a = e;
}