diff --git a/crates/napi/src/env.rs b/crates/napi/src/env.rs index 95eae3f5..71828f7b 100644 --- a/crates/napi/src/env.rs +++ b/crates/napi/src/env.rs @@ -834,7 +834,7 @@ impl Env { sys::napi_wrap( self.0, js_object.0.value, - Box::into_raw(Box::new(TaggedObject::new(native_object))) as *mut c_void, + Box::into_raw(Box::new(TaggedObject::new(native_object))).cast(), Some(raw_finalize::), ptr::null_mut(), ptr::null_mut(), @@ -1007,9 +1007,9 @@ impl Env { check_status!(unsafe { sys::napi_create_external( self.0, - Box::into_raw(Box::new(TaggedObject::new(native_object))) as *mut c_void, + Box::into_raw(Box::new(TaggedObject::new(native_object))).cast(), Some(raw_finalize::), - Box::into_raw(Box::new(size_hint)) as *mut c_void, + Box::into_raw(Box::new(size_hint)).cast(), &mut object_value, ) })?; diff --git a/crates/napi/src/error.rs b/crates/napi/src/error.rs index 90f96e03..db9543e9 100644 --- a/crates/napi/src/error.rs +++ b/crates/napi/src/error.rs @@ -15,21 +15,21 @@ use serde_json::Error as SerdeJSONError; use crate::bindgen_runtime::ToNapiValue; use crate::{check_status, sys, Env, JsUnknown, NapiValue, Status}; -pub type Result = std::result::Result; +pub type Result = std::result::Result>; /// Represent `JsError`. /// Return this Error in `js_function`, **napi-rs** will throw it as `JsError` for you. /// If you want throw it as `TypeError` or `RangeError`, you can use `JsTypeError/JsRangeError::from(Error).throw_into(env)` #[derive(Debug, Clone)] -pub struct Error { - pub status: Status, +pub struct Error = Status> { + pub status: S, pub reason: String, // Convert raw `JsError` into Error maybe_raw: sys::napi_ref, maybe_env: sys::napi_env, } -impl ToNapiValue for Error { +impl> ToNapiValue for Error { unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result { if val.maybe_raw.is_null() { let err = unsafe { JsError::from(val).into_value(env) }; @@ -109,17 +109,17 @@ impl fmt::Display for Error { } } -impl Error { - pub fn new(status: Status, reason: String) -> Self { +impl> Error { + pub fn new(status: S, reason: R) -> Self { Error { status, - reason, + reason: reason.to_string(), maybe_raw: ptr::null_mut(), maybe_env: ptr::null_mut(), } } - pub fn from_status(status: Status) -> Self { + pub fn from_status(status: S) -> Self { Error { status, reason: "".to_owned(), @@ -127,7 +127,9 @@ impl Error { maybe_env: ptr::null_mut(), } } +} +impl Error { pub fn from_reason>(reason: T) -> Self { Error { status: Status::GenericFailure, @@ -160,7 +162,7 @@ impl From for Error { } } -impl Drop for Error { +impl> Drop for Error { fn drop(&mut self) { #[cfg(not(feature = "noop"))] { @@ -201,7 +203,7 @@ impl TryFrom for ExtendedErrorInfo { } } -pub struct JsError(Error); +pub struct JsError = Status>(Error); #[cfg(feature = "anyhow")] impl From for JsError { @@ -210,16 +212,16 @@ impl From for JsError { } } -pub struct JsTypeError(Error); +pub struct JsTypeError = Status>(Error); -pub struct JsRangeError(Error); +pub struct JsRangeError = Status>(Error); #[cfg(feature = "experimental")] -pub struct JsSyntaxError(Error); +pub struct JsSyntaxError = Status>(Error); macro_rules! impl_object_methods { ($js_value:ident, $kind:expr) => { - impl $js_value { + impl> $js_value { /// # Safety /// /// This function is safety if env is not null ptr. @@ -235,7 +237,7 @@ macro_rules! impl_object_methods { return err; } - let error_status = format!("{:?}", self.0.status); + let error_status = self.0.status.as_ref(); let status_len = error_status.len(); let error_code_string = CString::new(error_status).unwrap(); let reason_len = self.0.reason.len(); @@ -267,8 +269,8 @@ macro_rules! impl_object_methods { pub unsafe fn throw_into(self, env: sys::napi_env) { #[cfg(debug_assertions)] let reason = self.0.reason.clone(); - let status = self.0.status; - if status == Status::PendingException { + let status = self.0.status.as_ref().to_string(); + if status == Status::PendingException.as_ref() { return; } let js_error = unsafe { self.into_value(env) }; @@ -281,13 +283,13 @@ macro_rules! impl_object_methods { "Throw error failed, status: [{}], raw message: \"{}\", raw status: [{}]", Status::from(throw_status), reason, - Status::from(status) + status ); } #[allow(clippy::not_unsafe_ptr_arg_deref)] pub fn throw(&self, env: sys::napi_env) -> Result<()> { - let error_status = format!("{:?}\0", self.0.status); + let error_status = format!("{:?}\0", self.0.status.as_ref()); let status_len = error_status.len(); let error_code_string = unsafe { CStr::from_bytes_with_nul_unchecked(error_status.as_bytes()) }; @@ -308,8 +310,8 @@ macro_rules! impl_object_methods { } } - impl From for $js_value { - fn from(err: Error) -> Self { + impl> From> for $js_value { + fn from(err: Error) -> Self { Self(err) } } diff --git a/crates/napi/src/status.rs b/crates/napi/src/status.rs index 0b20bcc3..f462b964 100644 --- a/crates/napi/src/status.rs +++ b/crates/napi/src/status.rs @@ -40,6 +40,37 @@ impl Display for Status { } } +impl AsRef for Status { + fn as_ref(&self) -> &str { + match self { + Status::Ok => "Ok", + Status::InvalidArg => "InvalidArg", + Status::ObjectExpected => "ObjectExpected", + Status::StringExpected => "StringExpected", + Status::NameExpected => "NameExpected", + Status::FunctionExpected => "FunctionExpected", + Status::NumberExpected => "NumberExpected", + Status::BooleanExpected => "BooleanExpected", + Status::ArrayExpected => "ArrayExpected", + Status::GenericFailure => "GenericFailure", + Status::PendingException => "PendingException", + Status::Cancelled => "Cancelled", + Status::EscapeCalledTwice => "EscapeCalledTwice", + Status::HandleScopeMismatch => "HandleScopeMismatch", + Status::CallbackScopeMismatch => "CallbackScopeMismatch", + Status::QueueFull => "QueueFull", + Status::Closing => "Closing", + Status::BigintExpected => "BigintExpected", + Status::DateExpected => "DateExpected", + Status::ArrayBufferExpected => "ArrayBufferExpected", + Status::DetachableArraybufferExpected => "DetachableArraybufferExpected", + Status::WouldDeadlock => "WouldDeadlock", + Status::NoExternalBuffersAllowed => "NoExternalBuffersAllowed", + _ => "Unknown", + } + } +} + impl From for Status { fn from(code: i32) -> Self { match code { diff --git a/examples/napi/__test__/typegen.spec.ts.md b/examples/napi/__test__/typegen.spec.ts.md index 7c627270..4bc5c775 100644 --- a/examples/napi/__test__/typegen.spec.ts.md +++ b/examples/napi/__test__/typegen.spec.ts.md @@ -110,6 +110,7 @@ Generated by [AVA](https://avajs.dev). export function throwError(): void␊ export function panic(): void␊ export function receiveString(s: string): string␊ + export function customStatusCode(): void␊ export function createExternal(size: number): ExternalObject␊ export function createExternalString(content: string): ExternalObject␊ export function getExternal(external: ExternalObject): number␊ diff --git a/examples/napi/__test__/typegen.spec.ts.snap b/examples/napi/__test__/typegen.spec.ts.snap index c299f346..8bcb74de 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 53624a31..474f2742 100644 --- a/examples/napi/__test__/values.spec.ts +++ b/examples/napi/__test__/values.spec.ts @@ -31,6 +31,7 @@ import { mapOption, readFile, throwError, + customStatusCode, panic, readPackageJson, getPackageJsonName, @@ -402,6 +403,12 @@ test('Result', (t) => { } }) +test('custom status code in Error', (t) => { + t.throws(() => customStatusCode(), { + code: 'Panic', + }) +}) + test('function ts type override', (t) => { t.deepEqual(tsRename({ foo: 1, bar: 2, baz: 2 }), ['foo', 'bar', 'baz']) }) diff --git a/examples/napi/index.d.ts b/examples/napi/index.d.ts index b76c0778..3f63dfb5 100644 --- a/examples/napi/index.d.ts +++ b/examples/napi/index.d.ts @@ -100,6 +100,7 @@ export function enumToI32(e: CustomNumEnum): number export function throwError(): void export function panic(): void export function receiveString(s: string): string +export function customStatusCode(): void export function createExternal(size: number): ExternalObject export function createExternalString(content: string): ExternalObject export function getExternal(external: ExternalObject): number diff --git a/examples/napi/src/error.rs b/examples/napi/src/error.rs index dbe41276..344a8ed0 100644 --- a/examples/napi/src/error.rs +++ b/examples/napi/src/error.rs @@ -14,3 +14,22 @@ pub fn panic() { pub fn receive_string(s: String) -> String { s } + +pub enum CustomError { + NapiError(Error), + Panic, +} + +impl AsRef for CustomError { + fn as_ref(&self) -> &str { + match self { + CustomError::Panic => "Panic", + CustomError::NapiError(e) => e.status.as_ref(), + } + } +} + +#[napi] +pub fn custom_status_code() -> Result<(), CustomError> { + Err(Error::new(CustomError::Panic, "don't panic")) +}