mirror of
https://github.com/napi-rs/napi-rs.git
synced 2025-12-08 19:56:07 +00:00
feat(napi): add Error.cause support to napi::Error (#2829)
Co-authored-by: LongYinan <lynweklm@gmail.com>
This commit is contained in:
parent
04aacf4abc
commit
7f5013eee3
@ -497,6 +497,7 @@ impl<T: FromNapiValue + 'static> futures_core::Stream for Reader<T> {
|
||||
*chunk = Err(Error {
|
||||
status: Status::GenericFailure,
|
||||
reason: "".to_string(),
|
||||
cause: None,
|
||||
maybe_raw: error_ref,
|
||||
maybe_env: cx.env.0,
|
||||
});
|
||||
|
||||
@ -12,6 +12,7 @@ use serde::{de, ser};
|
||||
#[cfg(feature = "serde-json")]
|
||||
use serde_json::Error as SerdeJSONError;
|
||||
|
||||
use crate::bindgen_runtime::JsObjectValue;
|
||||
use crate::{bindgen_runtime::ToNapiValue, check_status, sys, Env, JsValue, Status, Unknown};
|
||||
|
||||
pub type Result<T, S = Status> = std::result::Result<T, Error<S>>;
|
||||
@ -22,6 +23,7 @@ pub type Result<T, S = Status> = std::result::Result<T, Error<S>>;
|
||||
pub struct Error<S: AsRef<str> = Status> {
|
||||
pub status: S,
|
||||
pub reason: String,
|
||||
pub cause: Option<Box<Error>>,
|
||||
// Convert raw `JsError` into Error
|
||||
pub(crate) maybe_raw: sys::napi_ref,
|
||||
pub(crate) maybe_env: sys::napi_env,
|
||||
@ -47,6 +49,12 @@ impl<S: AsRef<str>> Drop for Error<S> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> Error<S> {
|
||||
pub fn set_cause(&mut self, cause: Error) {
|
||||
self.cause = Some(Box::new(cause));
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: AsRef<str>> std::fmt::Debug for Error<S> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(
|
||||
@ -134,10 +142,17 @@ impl From<Unknown<'_>> for Error {
|
||||
let maybe_error_message = value
|
||||
.coerce_to_string()
|
||||
.and_then(|a| a.into_utf8().and_then(|a| a.into_owned()));
|
||||
let maybe_cause: Option<Box<Error>> = value
|
||||
.coerce_to_object()
|
||||
.and_then(|obj| obj.get_named_property::<Unknown>("cause"))
|
||||
.map(|cause| Box::new(cause.into()))
|
||||
.ok();
|
||||
|
||||
if let Ok(error_message) = maybe_error_message {
|
||||
return Self {
|
||||
status: Status::GenericFailure,
|
||||
reason: error_message,
|
||||
cause: maybe_cause,
|
||||
maybe_raw: result,
|
||||
maybe_env,
|
||||
};
|
||||
@ -146,6 +161,7 @@ impl From<Unknown<'_>> for Error {
|
||||
Self {
|
||||
status: Status::GenericFailure,
|
||||
reason: "".to_string(),
|
||||
cause: maybe_cause,
|
||||
maybe_raw: result,
|
||||
maybe_env,
|
||||
}
|
||||
@ -174,6 +190,7 @@ impl<S: AsRef<str>> Error<S> {
|
||||
Error {
|
||||
status,
|
||||
reason: reason.to_string(),
|
||||
cause: None,
|
||||
maybe_raw: ptr::null_mut(),
|
||||
maybe_env: ptr::null_mut(),
|
||||
}
|
||||
@ -183,6 +200,7 @@ impl<S: AsRef<str>> Error<S> {
|
||||
Error {
|
||||
status,
|
||||
reason: "".to_owned(),
|
||||
cause: None,
|
||||
maybe_raw: ptr::null_mut(),
|
||||
maybe_env: ptr::null_mut(),
|
||||
}
|
||||
@ -200,6 +218,7 @@ impl<S: AsRef<str> + Clone> Error<S> {
|
||||
Ok(Self {
|
||||
status: self.status.clone(),
|
||||
reason: self.reason.to_string(),
|
||||
cause: None,
|
||||
maybe_raw: self.maybe_raw,
|
||||
maybe_env: self.maybe_env,
|
||||
})
|
||||
@ -211,6 +230,7 @@ impl Error {
|
||||
Error {
|
||||
status: Status::GenericFailure,
|
||||
reason: reason.into(),
|
||||
cause: None,
|
||||
maybe_raw: ptr::null_mut(),
|
||||
maybe_env: ptr::null_mut(),
|
||||
}
|
||||
@ -222,6 +242,7 @@ impl From<std::ffi::NulError> for Error {
|
||||
Error {
|
||||
status: Status::GenericFailure,
|
||||
reason: format!("{error}"),
|
||||
cause: None,
|
||||
maybe_raw: ptr::null_mut(),
|
||||
maybe_env: ptr::null_mut(),
|
||||
}
|
||||
@ -233,6 +254,7 @@ impl From<std::io::Error> for Error {
|
||||
Error {
|
||||
status: Status::GenericFailure,
|
||||
reason: format!("{error}"),
|
||||
cause: None,
|
||||
maybe_raw: ptr::null_mut(),
|
||||
maybe_env: ptr::null_mut(),
|
||||
}
|
||||
@ -382,6 +404,16 @@ macro_rules! impl_object_methods {
|
||||
debug_assert!(create_reason_status == sys::Status::napi_ok);
|
||||
let create_error_status = unsafe { $kind(env, error_code, reason_string, &mut js_error) };
|
||||
debug_assert!(create_error_status == sys::Status::napi_ok);
|
||||
if let Some(cause_error) = self.0.cause.take() {
|
||||
let cause = ToNapiValue::to_napi_value(env, *cause_error)
|
||||
.expect("Convert cause Error to napi_value should never error");
|
||||
let set_cause_status =
|
||||
unsafe { sys::napi_set_named_property(env, js_error, c"cause".as_ptr().cast(), cause) };
|
||||
debug_assert!(
|
||||
set_cause_status == sys::Status::napi_ok,
|
||||
"Set cause property failed"
|
||||
);
|
||||
}
|
||||
js_error
|
||||
}
|
||||
|
||||
|
||||
@ -755,6 +755,7 @@ unsafe extern "C" fn call_js_cb<
|
||||
Err(Error {
|
||||
maybe_raw: error_reference,
|
||||
maybe_env: raw_env,
|
||||
cause: None,
|
||||
status: Status::from(raw_status),
|
||||
reason,
|
||||
})
|
||||
|
||||
@ -986,6 +986,8 @@ Generated by [AVA](https://avajs.dev).
|
||||
␊
|
||||
export declare function throwError(): void␊
|
||||
␊
|
||||
export declare function throwErrorWithCause(): void␊
|
||||
␊
|
||||
export declare function throwSyntaxError(error: string, code?: string | undefined | null): void␊
|
||||
␊
|
||||
export declare function toJsObj(): object␊
|
||||
|
||||
Binary file not shown.
@ -58,6 +58,7 @@ import {
|
||||
mapOption,
|
||||
readFile,
|
||||
throwError,
|
||||
throwErrorWithCause,
|
||||
jsErrorCallback,
|
||||
customStatusCode,
|
||||
panic,
|
||||
@ -872,6 +873,9 @@ test('Option', (t) => {
|
||||
|
||||
test('Result', (t) => {
|
||||
t.throws(() => throwError(), void 0, 'Manual Error')
|
||||
const errorWithCause = t.throws(() => throwErrorWithCause())
|
||||
t.is(errorWithCause?.message, 'Manual Error')
|
||||
t.is((errorWithCause?.cause as Error)?.message, 'Inner Error')
|
||||
if (!process.env.SKIP_UNWIND_TEST) {
|
||||
t.throws(() => panic(), void 0, `Don't panic`)
|
||||
}
|
||||
|
||||
@ -343,6 +343,7 @@ export const threadsafeFunctionThrowError = __napiModule.exports.threadsafeFunct
|
||||
export const threadsafeFunctionThrowErrorWithStatus = __napiModule.exports.threadsafeFunctionThrowErrorWithStatus
|
||||
export const throwAsyncError = __napiModule.exports.throwAsyncError
|
||||
export const throwError = __napiModule.exports.throwError
|
||||
export const throwErrorWithCause = __napiModule.exports.throwErrorWithCause
|
||||
export const throwSyntaxError = __napiModule.exports.throwSyntaxError
|
||||
export const toJsObj = __napiModule.exports.toJsObj
|
||||
export const tsfnAsyncCall = __napiModule.exports.tsfnAsyncCall
|
||||
|
||||
@ -388,6 +388,7 @@ module.exports.threadsafeFunctionThrowError = __napiModule.exports.threadsafeFun
|
||||
module.exports.threadsafeFunctionThrowErrorWithStatus = __napiModule.exports.threadsafeFunctionThrowErrorWithStatus
|
||||
module.exports.throwAsyncError = __napiModule.exports.throwAsyncError
|
||||
module.exports.throwError = __napiModule.exports.throwError
|
||||
module.exports.throwErrorWithCause = __napiModule.exports.throwErrorWithCause
|
||||
module.exports.throwSyntaxError = __napiModule.exports.throwSyntaxError
|
||||
module.exports.toJsObj = __napiModule.exports.toJsObj
|
||||
module.exports.tsfnAsyncCall = __napiModule.exports.tsfnAsyncCall
|
||||
|
||||
@ -673,6 +673,7 @@ module.exports.threadsafeFunctionThrowError = nativeBinding.threadsafeFunctionTh
|
||||
module.exports.threadsafeFunctionThrowErrorWithStatus = nativeBinding.threadsafeFunctionThrowErrorWithStatus
|
||||
module.exports.throwAsyncError = nativeBinding.throwAsyncError
|
||||
module.exports.throwError = nativeBinding.throwError
|
||||
module.exports.throwErrorWithCause = nativeBinding.throwErrorWithCause
|
||||
module.exports.throwSyntaxError = nativeBinding.throwSyntaxError
|
||||
module.exports.toJsObj = nativeBinding.toJsObj
|
||||
module.exports.tsfnAsyncCall = nativeBinding.tsfnAsyncCall
|
||||
|
||||
@ -947,6 +947,8 @@ export declare function throwAsyncError(): Promise<void>
|
||||
|
||||
export declare function throwError(): void
|
||||
|
||||
export declare function throwErrorWithCause(): void
|
||||
|
||||
export declare function throwSyntaxError(error: string, code?: string | undefined | null): void
|
||||
|
||||
export declare function toJsObj(): object
|
||||
|
||||
@ -5,6 +5,13 @@ pub fn throw_error() -> Result<()> {
|
||||
Err(Error::new(Status::InvalidArg, "Manual Error".to_owned()))
|
||||
}
|
||||
|
||||
#[napi]
|
||||
pub fn throw_error_with_cause() -> Result<()> {
|
||||
let mut err = Error::new(Status::GenericFailure, "Manual Error".to_owned());
|
||||
err.set_cause(Error::new(Status::InvalidArg, "Inner Error".to_owned()));
|
||||
Err(err)
|
||||
}
|
||||
|
||||
#[napi(catch_unwind)]
|
||||
pub fn panic() {
|
||||
panic!("Don't panic");
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user