diff --git a/crates/backend/src/ast.rs b/crates/backend/src/ast.rs index 004707b5..6c71303c 100644 --- a/crates/backend/src/ast.rs +++ b/crates/backend/src/ast.rs @@ -7,7 +7,7 @@ pub struct NapiFn { pub js_name: String, pub attrs: Vec, pub args: Vec, - pub ret: Option, + pub ret: Option<(syn::Type, /* is_result */ bool)>, pub is_async: bool, pub fn_self: Option, pub kind: FnKind, diff --git a/crates/backend/src/codegen/fn.rs b/crates/backend/src/codegen/fn.rs index ce6b7221..5f2d1ab2 100644 --- a/crates/backend/src/codegen/fn.rs +++ b/crates/backend/src/codegen/fn.rs @@ -214,11 +214,22 @@ impl NapiFn { let js_name = &self.js_name; let ret_ty = &self.ret; - if self.kind == FnKind::Constructor { - quote! { cb.construct(#js_name, #ret) } - } else if let Some(ref ty) = ret_ty { - quote! { - <#ty as ToNapiValue>::to_napi_value(env, #ret) + if let Some((ref ty, is_result)) = ret_ty { + if self.kind == FnKind::Constructor { + quote! { cb.construct(#js_name, #ret) } + } else if *is_result { + 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 { quote! { diff --git a/crates/backend/src/typegen.rs b/crates/backend/src/typegen.rs index 39e77169..2ee8b960 100644 --- a/crates/backend/src/typegen.rs +++ b/crates/backend/src/typegen.rs @@ -40,29 +40,30 @@ pub static TYPE_REGEXES: Lazy> = Lazy::new(|| { 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 { - 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) => { format!( "[{}]", tuple .elems .iter() - .map(|elem| ty_to_ts_type(elem)) + .map(|elem| ty_to_ts_type(elem, false)) .collect::>() .join(", ") ) } - Type::Path(syn::TypePath { qself: None, path }) => { - str_to_ts_type(path.to_token_stream().to_string().as_str()) - } + Type::Path(syn::TypePath { qself: None, path }) => str_to_ts_type( + path.to_token_stream().to_string().as_str(), + omit_top_level_result, + ), _ => "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 { "()" => "undefined".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 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) => { let captures = TYPE_REGEXES["Option"].captures(s).unwrap(); 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) => { let captures = TYPE_REGEXES["Result"].captures(s).unwrap(); let inner = captures.get(1).unwrap().as_str(); - - format!("Error | {}", str_to_ts_type(inner)) + if omit_top_level_result { + str_to_ts_type(inner, false) + } else { + format!("Error | {}", str_to_ts_type(inner, false)) + } } s => s.to_owned(), } diff --git a/crates/backend/src/typegen/fn.rs b/crates/backend/src/typegen/fn.rs index 2d6a0ae3..4b5358e4 100644 --- a/crates/backend/src/typegen/fn.rs +++ b/crates/backend/src/typegen/fn.rs @@ -29,11 +29,11 @@ fn gen_callback_type(callback: &CallbackArg) -> String { .args .iter() .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::>() .join(", "), ret = match &callback.ret { - Some(ty) => ty_to_ts_type(ty), + Some(ty) => ty_to_ts_type(ty, true), None => "void".to_owned(), } ) @@ -51,7 +51,7 @@ impl NapiFn { } let mut arg = path.pat.to_token_stream().to_string().to_case(Case::Camel); arg.push_str(": "); - arg.push_str(&ty_to_ts_type(&path.ty)); + arg.push_str(&ty_to_ts_type(&path.ty, false)); Some(arg) } @@ -87,8 +87,13 @@ impl NapiFn { match self.kind { FnKind::Constructor | FnKind::Setter => "".to_owned(), _ => { - let ret = if let Some(ref ret) = self.ret { - ty_to_ts_type(ret) + let ret = if let Some((ref ret, is_result)) = self.ret { + let ts_type = ty_to_ts_type(ret, is_result); + if ts_type == "undefined" { + "void".to_owned() + } else { + ts_type + } } else { "void".to_owned() }; diff --git a/crates/backend/src/typegen/struct.rs b/crates/backend/src/typegen/struct.rs index 406636fd..e20117cb 100644 --- a/crates/backend/src/typegen/struct.rs +++ b/crates/backend/src/typegen/struct.rs @@ -39,7 +39,7 @@ impl NapiStruct { if !f.setter { 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 { ctor_args.push(arg.clone()); } diff --git a/crates/macro/src/parser/mod.rs b/crates/macro/src/parser/mod.rs index 13a1ae8f..d793b0d0 100644 --- a/crates/macro/src/parser/mod.rs +++ b/crates/macro/src/parser/mod.rs @@ -520,7 +520,10 @@ fn napi_fn_from_decl( let ret = match output { 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(|_| { diff --git a/crates/napi/src/bindgen_runtime/callback_info.rs b/crates/napi/src/bindgen_runtime/callback_info.rs index c71a2762..e85824ae 100644 --- a/crates/napi/src/bindgen_runtime/callback_info.rs +++ b/crates/napi/src/bindgen_runtime/callback_info.rs @@ -47,15 +47,10 @@ impl CallbackInfo { } pub fn get_arg(&self, index: usize) -> sys::napi_value { - *self - .args - .get(index) - .unwrap_or_else(|| panic!("index {} must < {}", index, N)) + self.args[index] } pub fn this(&self) -> sys::napi_value { - debug_assert!(!self.this.is_null()); - self.this } diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 9584e886..8981df32 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -16,7 +16,7 @@ Generated by [AVA](https://avajs.dev). 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 function enumToI32(e: CustomNumEnum): number␊ - export function getError(): Error | undefined␊ + export function throwError(): void␊ export function mapOption(val: number | null): number | null␊ export function add(a: number, b: number): number␊ export function fibonacci(n: number): number␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index 55643bb7..095ccffd 100644 Binary files a/examples/napi/__test__/typegen.spec.ts.snap and b/examples/napi/__test__/typegen.spec.ts.snap differ diff --git a/examples/napi/__test__/values.spec.ts b/examples/napi/__test__/values.spec.ts index c7487f0a..23810e19 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -19,7 +19,7 @@ import { createObj, mapOption, readFile, - getError, + throwError, } from '../' test('number', (t) => { @@ -98,7 +98,5 @@ test('Option', (t) => { }) test('Result', (t) => { - const error = getError() - t.not(error, undefined) - t.is(error!.message, 'Manual Error') + t.throws(() => throwError(), null, 'Manual Error') }) diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index 79530ac3..aae4452d 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -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 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 getError(): Error | undefined +export function throwError(): void export function mapOption(val: number | null): number | null export function add(a: number, b: number): number export function fibonacci(n: number): number diff --git a/examples/napi/src/error.rs b/examples/napi/src/error.rs index 31ce7e4b..e1533117 100644 --- a/examples/napi/src/error.rs +++ b/examples/napi/src/error.rs @@ -1,6 +1,6 @@ use napi::bindgen_prelude::*; #[napi] -fn get_error() -> Result<()> { +fn throw_error() -> Result<()> { Err(Error::new(Status::InvalidArg, "Manual Error".to_owned())) }