commit
8b77bc62ab
13 changed files with 132 additions and 13 deletions
|
@ -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'
|
||||||
|
|
|
@ -239,20 +239,40 @@ 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)?; });
|
||||||
|
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)); });
|
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)?; });
|
||||||
|
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)); });
|
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 {
|
let destructed_fields = if self.is_tuple {
|
||||||
quote! {
|
quote! {
|
||||||
|
|
|
@ -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)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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! {
|
||||||
|
|
|
@ -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)?)
|
||||||
|
|
|
@ -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 {␊
|
||||||
|
|
Binary file not shown.
|
@ -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')
|
||||||
|
|
14
examples/napi/index.d.ts
vendored
14
examples/napi/index.d.ts
vendored
|
@ -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 {
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue