throw if napi function returns Err variant of Result<T>
This commit is contained in:
parent
ee7a146ea1
commit
f66f79e587
12 changed files with 53 additions and 37 deletions
|
@ -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,
|
||||||
|
|
|
@ -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! {
|
||||||
|
|
|
@ -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(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
};
|
};
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(|_| {
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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␊
|
||||||
|
|
Binary file not shown.
|
@ -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')
|
|
||||||
})
|
})
|
||||||
|
|
2
examples/napi/index.d.ts
vendored
2
examples/napi/index.d.ts
vendored
|
@ -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
|
||||||
|
|
|
@ -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()))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue