throw if napi function returns Err variant of Result<T>

This commit is contained in:
forehalo 2021-09-24 14:45:27 +08:00 committed by LongYinan
parent ee7a146ea1
commit f66f79e587
12 changed files with 53 additions and 37 deletions

View file

@ -7,7 +7,7 @@ pub struct NapiFn {
pub js_name: String, pub js_name: String,
pub attrs: Vec<Attribute>, pub attrs: Vec<Attribute>,
pub args: Vec<NapiFnArgKind>, pub args: Vec<NapiFnArgKind>,
pub ret: Option<syn::Type>, pub ret: Option<(syn::Type, /* is_result */ bool)>,
pub is_async: bool, pub is_async: bool,
pub fn_self: Option<FnSelf>, pub fn_self: Option<FnSelf>,
pub kind: FnKind, pub kind: FnKind,

View file

@ -214,11 +214,22 @@ impl NapiFn {
let js_name = &self.js_name; let js_name = &self.js_name;
let ret_ty = &self.ret; let ret_ty = &self.ret;
if self.kind == FnKind::Constructor { if let Some((ref ty, is_result)) = ret_ty {
quote! { cb.construct(#js_name, #ret) } if self.kind == FnKind::Constructor {
} else if let Some(ref ty) = ret_ty { quote! { cb.construct(#js_name, #ret) }
quote! { } else if *is_result {
<#ty as ToNapiValue>::to_napi_value(env, #ret) quote! {
if #ret.is_ok() {
<#ty as ToNapiValue>::to_napi_value(env, #ret)
} else {
JsError::from(#ret.unwrap_err()).throw_into(env);
Ok(std::ptr::null_mut())
}
}
} else {
quote! {
<#ty as ToNapiValue>::to_napi_value(env, #ret)
}
} }
} else { } else {
quote! { quote! {

View file

@ -40,29 +40,30 @@ pub static TYPE_REGEXES: Lazy<HashMap<&'static str, Regex>> = Lazy::new(|| {
map map
}); });
pub fn ty_to_ts_type(ty: &Type) -> String { pub fn ty_to_ts_type(ty: &Type, omit_top_level_result: bool) -> String {
match ty { match ty {
Type::Reference(r) => ty_to_ts_type(&r.elem), Type::Reference(r) => ty_to_ts_type(&r.elem, omit_top_level_result),
Type::Tuple(tuple) => { Type::Tuple(tuple) => {
format!( format!(
"[{}]", "[{}]",
tuple tuple
.elems .elems
.iter() .iter()
.map(|elem| ty_to_ts_type(elem)) .map(|elem| ty_to_ts_type(elem, false))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", ")
) )
} }
Type::Path(syn::TypePath { qself: None, path }) => { Type::Path(syn::TypePath { qself: None, path }) => str_to_ts_type(
str_to_ts_type(path.to_token_stream().to_string().as_str()) path.to_token_stream().to_string().as_str(),
} omit_top_level_result,
),
_ => "any".to_owned(), _ => "any".to_owned(),
} }
} }
pub fn str_to_ts_type(ty: &str) -> String { pub fn str_to_ts_type(ty: &str, omit_top_level_result: bool) -> String {
match ty { match ty {
"()" => "undefined".to_owned(), "()" => "undefined".to_owned(),
"i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" => "number".to_owned(), "i8" | "i16" | "i32" | "i64" | "u8" | "u16" | "u32" => "number".to_owned(),
@ -76,19 +77,22 @@ pub fn str_to_ts_type(ty: &str) -> String {
let captures = TYPE_REGEXES["Vec"].captures(s).unwrap(); let captures = TYPE_REGEXES["Vec"].captures(s).unwrap();
let inner = captures.get(1).unwrap().as_str(); let inner = captures.get(1).unwrap().as_str();
format!("Array<{}>", str_to_ts_type(inner)) format!("Array<{}>", str_to_ts_type(inner, false))
} }
s if s.starts_with("Option") && TYPE_REGEXES["Option"].is_match(s) => { s if s.starts_with("Option") && TYPE_REGEXES["Option"].is_match(s) => {
let captures = TYPE_REGEXES["Option"].captures(s).unwrap(); let captures = TYPE_REGEXES["Option"].captures(s).unwrap();
let inner = captures.get(1).unwrap().as_str(); let inner = captures.get(1).unwrap().as_str();
format!("{} | null", str_to_ts_type(inner)) format!("{} | null", str_to_ts_type(inner, false))
} }
s if s.starts_with("Result") && TYPE_REGEXES["Result"].is_match(s) => { s if s.starts_with("Result") && TYPE_REGEXES["Result"].is_match(s) => {
let captures = TYPE_REGEXES["Result"].captures(s).unwrap(); let captures = TYPE_REGEXES["Result"].captures(s).unwrap();
let inner = captures.get(1).unwrap().as_str(); let inner = captures.get(1).unwrap().as_str();
if omit_top_level_result {
format!("Error | {}", str_to_ts_type(inner)) str_to_ts_type(inner, false)
} else {
format!("Error | {}", str_to_ts_type(inner, false))
}
} }
s => s.to_owned(), s => s.to_owned(),
} }

View file

@ -29,11 +29,11 @@ fn gen_callback_type(callback: &CallbackArg) -> String {
.args .args
.iter() .iter()
.enumerate() .enumerate()
.map(|(i, arg)| { format!("arg{}: {}", i, ty_to_ts_type(arg)) }) .map(|(i, arg)| { format!("arg{}: {}", i, ty_to_ts_type(arg, false)) })
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", "), .join(", "),
ret = match &callback.ret { ret = match &callback.ret {
Some(ty) => ty_to_ts_type(ty), Some(ty) => ty_to_ts_type(ty, true),
None => "void".to_owned(), None => "void".to_owned(),
} }
) )
@ -51,7 +51,7 @@ impl NapiFn {
} }
let mut arg = path.pat.to_token_stream().to_string().to_case(Case::Camel); let mut arg = path.pat.to_token_stream().to_string().to_case(Case::Camel);
arg.push_str(": "); arg.push_str(": ");
arg.push_str(&ty_to_ts_type(&path.ty)); arg.push_str(&ty_to_ts_type(&path.ty, false));
Some(arg) Some(arg)
} }
@ -87,8 +87,13 @@ impl NapiFn {
match self.kind { match self.kind {
FnKind::Constructor | FnKind::Setter => "".to_owned(), FnKind::Constructor | FnKind::Setter => "".to_owned(),
_ => { _ => {
let ret = if let Some(ref ret) = self.ret { let ret = if let Some((ref ret, is_result)) = self.ret {
ty_to_ts_type(ret) let ts_type = ty_to_ts_type(ret, is_result);
if ts_type == "undefined" {
"void".to_owned()
} else {
ts_type
}
} else { } else {
"void".to_owned() "void".to_owned()
}; };

View file

@ -39,7 +39,7 @@ impl NapiStruct {
if !f.setter { if !f.setter {
field_str.push_str("readonly ") field_str.push_str("readonly ")
} }
let arg = format!("{}: {}", &f.js_name, ty_to_ts_type(&f.ty)); let arg = format!("{}: {}", &f.js_name, ty_to_ts_type(&f.ty, false));
if self.gen_default_ctor { if self.gen_default_ctor {
ctor_args.push(arg.clone()); ctor_args.push(arg.clone());
} }

View file

@ -520,7 +520,10 @@ fn napi_fn_from_decl(
let ret = match output { let ret = match output {
syn::ReturnType::Default => None, syn::ReturnType::Default => None,
syn::ReturnType::Type(_, ty) => Some(replace_self(*ty, parent)), syn::ReturnType::Type(_, ty) => {
let is_result = ty.to_token_stream().to_string().starts_with("Result <");
Some((replace_self(*ty, parent), is_result))
}
}; };
Diagnostic::from_vec(errors).map(|_| { Diagnostic::from_vec(errors).map(|_| {

View file

@ -47,15 +47,10 @@ impl<const N: usize> CallbackInfo<N> {
} }
pub fn get_arg(&self, index: usize) -> sys::napi_value { pub fn get_arg(&self, index: usize) -> sys::napi_value {
*self self.args[index]
.args
.get(index)
.unwrap_or_else(|| panic!("index {} must < {}", index, N))
} }
pub fn this(&self) -> sys::napi_value { pub fn this(&self) -> sys::napi_value {
debug_assert!(!self.this.is_null());
self.this self.this
} }

View file

@ -16,7 +16,7 @@ Generated by [AVA](https://avajs.dev).
export enum Kind { Dog = 0, Cat = 1, Duck = 2 }␊ export enum Kind { Dog = 0, Cat = 1, Duck = 2 }␊
export enum CustomNumEnum { One = 1, Two = 2, Three = 3, Four = 4, Six = 6, Eight = 8, Nine = 9, Ten = 10 }␊ export enum CustomNumEnum { One = 1, Two = 2, Three = 3, Four = 4, Six = 6, Eight = 8, Nine = 9, Ten = 10 }␊
export function enumToI32(e: CustomNumEnum): number␊ export function enumToI32(e: CustomNumEnum): number␊
export function getError(): Error | undefined␊ export function throwError(): void␊
export function mapOption(val: number | null): number | null␊ export function mapOption(val: number | null): number | null␊
export function add(a: number, b: number): number␊ export function add(a: number, b: number): number␊
export function fibonacci(n: number): number␊ export function fibonacci(n: number): number␊

View file

@ -19,7 +19,7 @@ import {
createObj, createObj,
mapOption, mapOption,
readFile, readFile,
getError, throwError,
} from '../' } from '../'
test('number', (t) => { test('number', (t) => {
@ -98,7 +98,5 @@ test('Option', (t) => {
}) })
test('Result', (t) => { test('Result', (t) => {
const error = getError() t.throws(() => throwError(), null, 'Manual Error')
t.not(error, undefined)
t.is(error!.message, 'Manual Error')
}) })

View file

@ -6,7 +6,7 @@ export function readFile(callback: (arg0: Error | undefined, arg1: string | null
export enum Kind { Dog = 0, Cat = 1, Duck = 2 } export enum Kind { Dog = 0, Cat = 1, Duck = 2 }
export enum CustomNumEnum { One = 1, Two = 2, Three = 3, Four = 4, Six = 6, Eight = 8, Nine = 9, Ten = 10 } export enum CustomNumEnum { One = 1, Two = 2, Three = 3, Four = 4, Six = 6, Eight = 8, Nine = 9, Ten = 10 }
export function enumToI32(e: CustomNumEnum): number export function enumToI32(e: CustomNumEnum): number
export function getError(): Error | undefined export function throwError(): void
export function mapOption(val: number | null): number | null export function mapOption(val: number | null): number | null
export function add(a: number, b: number): number export function add(a: number, b: number): number
export function fibonacci(n: number): number export function fibonacci(n: number): number

View file

@ -1,6 +1,6 @@
use napi::bindgen_prelude::*; use napi::bindgen_prelude::*;
#[napi] #[napi]
fn get_error() -> Result<()> { fn throw_error() -> Result<()> {
Err(Error::new(Status::InvalidArg, "Manual Error".to_owned())) Err(Error::new(Status::InvalidArg, "Manual Error".to_owned()))
} }