1use std::convert::{From, TryFrom};
2use std::error;
3use std::ffi::CString;
4use std::fmt;
5#[cfg(feature = "serde-json")]
6use std::fmt::Display;
7use std::os::raw::{c_char, c_void};
8use std::ptr;
9
10#[cfg(feature = "serde-json")]
11use serde::{de, ser};
12#[cfg(feature = "serde-json")]
13use serde_json::Error as SerdeJSONError;
14
15use crate::bindgen_runtime::JsObjectValue;
16use crate::{bindgen_runtime::ToNapiValue, check_status, sys, Env, JsValue, Status, Unknown};
17
18pub type Result<T, S = Status> = std::result::Result<T, Error<S>>;
19
20pub struct Error<S: AsRef<str> = Status> {
24 pub status: S,
25 pub reason: String,
26 pub cause: Option<Box<Error>>,
27 pub(crate) maybe_raw: sys::napi_ref,
29 pub(crate) maybe_env: sys::napi_env,
30}
31
32impl<S: AsRef<str>> Drop for Error<S> {
33 fn drop(&mut self) {
34 if !self.maybe_raw.is_null() {
36 let mut ref_count = 0;
37 let status =
38 unsafe { sys::napi_reference_unref(self.maybe_env, self.maybe_raw, &mut ref_count) };
39 if status != sys::Status::napi_ok {
40 eprintln!("unref error reference failed: {}", Status::from(status));
41 }
42 if ref_count == 0 {
43 let status = unsafe { sys::napi_delete_reference(self.maybe_env, self.maybe_raw) };
44 if status != sys::Status::napi_ok {
45 eprintln!("delete error reference failed: {}", Status::from(status));
46 }
47 }
48 }
49 }
50}
51
52impl<S: AsRef<str>> Error<S> {
53 pub fn set_cause(&mut self, cause: Error) {
54 self.cause = Some(Box::new(cause));
55 }
56}
57
58impl<S: AsRef<str>> std::fmt::Debug for Error<S> {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 write!(
61 f,
62 "Error {{ status: {:?}, reason: {:?} }}",
63 self.status.as_ref(),
64 self.reason
65 )
66 }
67}
68
69impl<S: AsRef<str>> ToNapiValue for Error<S> {
70 unsafe fn to_napi_value(env: sys::napi_env, mut val: Self) -> Result<sys::napi_value> {
71 if val.maybe_raw.is_null() {
72 let err = unsafe { JsError::from(val).into_value(env) };
73 Ok(err)
74 } else {
75 let mut value = std::ptr::null_mut();
76 check_status!(
77 unsafe { sys::napi_get_reference_value(env, val.maybe_raw, &mut value) },
78 "Get error reference in `to_napi_value` failed"
79 )?;
80 let mut ref_count = 0;
81 check_status!(
82 unsafe { sys::napi_reference_unref(env, val.maybe_raw, &mut ref_count) },
83 "Unref error reference in `to_napi_value` failed"
84 )?;
85 if ref_count == 0 {
86 check_status!(
87 unsafe { sys::napi_delete_reference(env, val.maybe_raw) },
88 "Delete error reference in `to_napi_value` failed"
89 )?;
90 }
91 val.maybe_raw = ptr::null_mut();
93 val.maybe_env = ptr::null_mut();
94 Ok(value)
95 }
96 }
97}
98
99unsafe impl<S> Send for Error<S> where S: Send + AsRef<str> {}
100unsafe impl<S> Sync for Error<S> where S: Sync + AsRef<str> {}
101
102impl<S: AsRef<str> + std::fmt::Debug> error::Error for Error<S> {}
103
104impl<S: AsRef<str>> From<std::convert::Infallible> for Error<S> {
105 fn from(_: std::convert::Infallible) -> Self {
106 unreachable!()
107 }
108}
109
110#[cfg(feature = "serde-json")]
111impl ser::Error for Error {
112 fn custom<T: Display>(msg: T) -> Self {
113 Error::new(Status::InvalidArg, msg.to_string())
114 }
115}
116
117#[cfg(feature = "serde-json")]
118impl de::Error for Error {
119 fn custom<T: Display>(msg: T) -> Self {
120 Error::new(Status::InvalidArg, msg.to_string())
121 }
122}
123
124#[cfg(feature = "serde-json")]
125impl From<SerdeJSONError> for Error {
126 fn from(value: SerdeJSONError) -> Self {
127 Error::new(Status::InvalidArg, format!("{value}"))
128 }
129}
130
131impl From<Unknown<'_>> for Error {
132 fn from(value: Unknown) -> Self {
133 let mut result = std::ptr::null_mut();
134 let status = unsafe { sys::napi_create_reference(value.0.env, value.0.value, 1, &mut result) };
135 if status != sys::Status::napi_ok {
136 return Error::new(
137 Status::from(status),
138 "Create Error reference failed".to_owned(),
139 );
140 }
141 let maybe_env = value.0.env;
142 let maybe_error_message = value
143 .coerce_to_string()
144 .and_then(|a| a.into_utf8().and_then(|a| a.into_owned()));
145 let maybe_cause: Option<Box<Error>> = value
146 .coerce_to_object()
147 .and_then(|obj| obj.get_named_property::<Unknown>("cause"))
148 .map(|cause| Box::new(cause.into()))
149 .ok();
150
151 if let Ok(error_message) = maybe_error_message {
152 return Self {
153 status: Status::GenericFailure,
154 reason: error_message,
155 cause: maybe_cause,
156 maybe_raw: result,
157 maybe_env,
158 };
159 }
160
161 Self {
162 status: Status::GenericFailure,
163 reason: "".to_string(),
164 cause: maybe_cause,
165 maybe_raw: result,
166 maybe_env,
167 }
168 }
169}
170
171#[cfg(feature = "anyhow")]
172impl From<anyhow::Error> for Error {
173 fn from(value: anyhow::Error) -> Self {
174 Error::new(Status::GenericFailure, format!("{:?}", value))
175 }
176}
177
178impl<S: AsRef<str> + std::fmt::Debug> fmt::Display for Error<S> {
179 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180 if !self.reason.is_empty() {
181 write!(f, "{:?}, {}", self.status, self.reason)
182 } else {
183 write!(f, "{:?}", self.status)
184 }
185 }
186}
187
188impl<S: AsRef<str>> Error<S> {
189 pub fn new<R: ToString>(status: S, reason: R) -> Self {
190 Error {
191 status,
192 reason: reason.to_string(),
193 cause: None,
194 maybe_raw: ptr::null_mut(),
195 maybe_env: ptr::null_mut(),
196 }
197 }
198
199 pub fn from_status(status: S) -> Self {
200 Error {
201 status,
202 reason: "".to_owned(),
203 cause: None,
204 maybe_raw: ptr::null_mut(),
205 maybe_env: ptr::null_mut(),
206 }
207 }
208}
209
210impl<S: AsRef<str> + Clone> Error<S> {
211 pub fn try_clone(&self) -> Result<Self> {
212 if !self.maybe_raw.is_null() {
213 check_status!(
214 unsafe { sys::napi_reference_ref(self.maybe_env, self.maybe_raw, &mut 0) },
215 "Failed to increase error reference count"
216 )?;
217 }
218 Ok(Self {
219 status: self.status.clone(),
220 reason: self.reason.to_string(),
221 cause: None,
222 maybe_raw: self.maybe_raw,
223 maybe_env: self.maybe_env,
224 })
225 }
226}
227
228impl Error {
229 pub fn from_reason<T: Into<String>>(reason: T) -> Self {
230 Error {
231 status: Status::GenericFailure,
232 reason: reason.into(),
233 cause: None,
234 maybe_raw: ptr::null_mut(),
235 maybe_env: ptr::null_mut(),
236 }
237 }
238}
239
240impl From<std::ffi::NulError> for Error {
241 fn from(error: std::ffi::NulError) -> Self {
242 Error {
243 status: Status::GenericFailure,
244 reason: format!("{error}"),
245 cause: None,
246 maybe_raw: ptr::null_mut(),
247 maybe_env: ptr::null_mut(),
248 }
249 }
250}
251
252impl From<std::io::Error> for Error {
253 fn from(error: std::io::Error) -> Self {
254 Error {
255 status: Status::GenericFailure,
256 reason: format!("{error}"),
257 cause: None,
258 maybe_raw: ptr::null_mut(),
259 maybe_env: ptr::null_mut(),
260 }
261 }
262}
263
264#[derive(Clone, Debug)]
265pub struct ExtendedErrorInfo {
266 pub message: String,
267 pub engine_reserved: *mut c_void,
268 pub engine_error_code: u32,
269 pub error_code: Status,
270}
271
272impl TryFrom<sys::napi_extended_error_info> for ExtendedErrorInfo {
273 type Error = Error;
274
275 fn try_from(value: sys::napi_extended_error_info) -> Result<Self> {
276 Ok(Self {
277 message: unsafe {
278 CString::from_raw(value.error_message as *mut c_char)
279 .into_string()
280 .map_err(|e| Error::new(Status::GenericFailure, format!("{e}")))?
281 },
282 engine_error_code: value.engine_error_code,
283 engine_reserved: value.engine_reserved,
284 error_code: Status::from(value.error_code),
285 })
286 }
287}
288
289pub struct JsError<S: AsRef<str> = Status>(Error<S>);
290
291#[cfg(feature = "anyhow")]
292impl From<anyhow::Error> for JsError {
293 fn from(value: anyhow::Error) -> Self {
294 JsError(Error::new(Status::GenericFailure, value.to_string()))
295 }
296}
297
298pub struct JsTypeError<S: AsRef<str> = Status>(Error<S>);
299
300pub struct JsRangeError<S: AsRef<str> = Status>(Error<S>);
301
302#[cfg(feature = "napi9")]
303pub struct JsSyntaxError<S: AsRef<str> = Status>(Error<S>);
304
305pub(crate) fn get_error_message_and_stack_trace(
306 env: sys::napi_env,
307 err: sys::napi_value,
308) -> Result<String> {
309 use crate::bindgen_runtime::FromNapiValue;
310
311 let mut error_string = ptr::null_mut();
312 check_status!(
313 unsafe { sys::napi_coerce_to_string(env, err, &mut error_string) },
314 "Get error message failed"
315 )?;
316 let mut result = unsafe { String::from_napi_value(env, error_string) }?;
317
318 let mut stack_trace = ptr::null_mut();
319 check_status!(
320 unsafe { sys::napi_get_named_property(env, err, c"stack".as_ptr().cast(), &mut stack_trace) },
321 "Get stack trace failed"
322 )?;
323 let mut stack_type = -1;
324 check_status!(
325 unsafe { sys::napi_typeof(env, stack_trace, &mut stack_type) },
326 "Get stack trace type failed"
327 )?;
328 if stack_type == sys::ValueType::napi_string {
329 let stack_trace = unsafe { String::from_napi_value(env, stack_trace) }?;
330 result.push('\n');
331 result.push_str(&stack_trace);
332 }
333
334 Ok(result)
335}
336
337macro_rules! impl_object_methods {
338 ($js_value:ident, $kind:expr) => {
339 impl<S: AsRef<str>> $js_value<S> {
340 pub unsafe fn into_value(mut self, env: sys::napi_env) -> sys::napi_value {
344 if !self.0.maybe_raw.is_null() {
345 let mut err = ptr::null_mut();
346 let get_err_status =
347 unsafe { sys::napi_get_reference_value(env, self.0.maybe_raw, &mut err) };
348 debug_assert!(
349 get_err_status == sys::Status::napi_ok,
350 "Get Error from Reference failed"
351 );
352 let mut ref_count = 0;
353 let unref_status =
354 unsafe { sys::napi_reference_unref(env, self.0.maybe_raw, &mut ref_count) };
355 debug_assert!(
356 unref_status == sys::Status::napi_ok,
357 "Unref Error Reference failed"
358 );
359 if ref_count == 0 {
360 let delete_err_status = unsafe { sys::napi_delete_reference(env, self.0.maybe_raw) };
361 debug_assert!(
362 delete_err_status == sys::Status::napi_ok,
363 "Delete Error Reference failed"
364 );
365 }
366 self.0.maybe_raw = ptr::null_mut();
368 self.0.maybe_env = ptr::null_mut();
369 let mut is_error = false;
370 let is_error_status = unsafe { sys::napi_is_error(env, err, &mut is_error) };
371 debug_assert!(
372 is_error_status == sys::Status::napi_ok,
373 "Check Error failed"
374 );
375 if is_error {
377 return err;
378 }
379 }
380
381 let error_status = self.0.status.as_ref();
382 let status_len = error_status.len();
383 let reason_len = self.0.reason.len();
384 let mut error_code = ptr::null_mut();
385 let mut reason_string = ptr::null_mut();
386 let mut js_error = ptr::null_mut();
387 let create_code_status = unsafe {
388 sys::napi_create_string_utf8(
389 env,
390 error_status.as_ptr().cast(),
391 status_len as isize,
392 &mut error_code,
393 )
394 };
395 debug_assert!(create_code_status == sys::Status::napi_ok);
396 let create_reason_status = unsafe {
397 sys::napi_create_string_utf8(
398 env,
399 self.0.reason.as_ptr().cast(),
400 reason_len as isize,
401 &mut reason_string,
402 )
403 };
404 debug_assert!(create_reason_status == sys::Status::napi_ok);
405 let create_error_status = unsafe { $kind(env, error_code, reason_string, &mut js_error) };
406 debug_assert!(create_error_status == sys::Status::napi_ok);
407 if let Some(cause_error) = self.0.cause.take() {
408 let cause = ToNapiValue::to_napi_value(env, *cause_error)
409 .expect("Convert cause Error to napi_value should never error");
410 let set_cause_status =
411 unsafe { sys::napi_set_named_property(env, js_error, c"cause".as_ptr().cast(), cause) };
412 debug_assert!(
413 set_cause_status == sys::Status::napi_ok,
414 "Set cause property failed"
415 );
416 }
417 js_error
418 }
419
420 pub fn into_unknown<'env>(self, env: Env) -> Unknown<'env> {
421 let value = unsafe { self.into_value(env.raw()) };
422 unsafe { Unknown::from_raw_unchecked(env.raw(), value) }
423 }
424
425 pub unsafe fn throw_into(self, env: sys::napi_env) {
429 #[cfg(debug_assertions)]
430 let reason = self.0.reason.clone();
431 let status = self.0.status.as_ref().to_string();
432 if status == Status::PendingException.as_ref() {
434 return;
435 }
436 let mut is_pending_exception = false;
438 assert_eq!(
439 unsafe { $crate::sys::napi_is_exception_pending(env, &mut is_pending_exception) },
440 $crate::sys::Status::napi_ok,
441 "Check exception status failed"
442 );
443 let js_error = match is_pending_exception {
444 true => {
445 let mut error_result = std::ptr::null_mut();
446 assert_eq!(
447 unsafe { $crate::sys::napi_get_and_clear_last_exception(env, &mut error_result) },
448 $crate::sys::Status::napi_ok,
449 "Get and clear last exception failed"
450 );
451 error_result
452 }
453 false => unsafe { self.into_value(env) },
454 };
455 #[cfg(debug_assertions)]
456 let throw_status = unsafe { sys::napi_throw(env, js_error) };
457 unsafe { sys::napi_throw(env, js_error) };
458 #[cfg(debug_assertions)]
459 assert!(
460 throw_status == sys::Status::napi_ok,
461 "Throw error failed, status: [{}], raw message: \"{}\", raw status: [{}]",
462 Status::from(throw_status),
463 reason,
464 status
465 );
466 }
467 }
468
469 impl<S: AsRef<str>> From<Error<S>> for $js_value<S> {
470 fn from(err: Error<S>) -> Self {
471 Self(err)
472 }
473 }
474
475 impl crate::bindgen_prelude::ToNapiValue for $js_value {
476 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
477 unsafe { ToNapiValue::to_napi_value(env, val.0) }
478 }
479 }
480 };
481}
482
483impl_object_methods!(JsError, sys::napi_create_error);
484impl_object_methods!(JsTypeError, sys::napi_create_type_error);
485impl_object_methods!(JsRangeError, sys::napi_create_range_error);
486#[cfg(feature = "napi9")]
487impl_object_methods!(JsSyntaxError, sys::node_api_create_syntax_error);
488
489#[doc(hidden)]
490#[macro_export]
491macro_rules! error {
492 ($status:expr, $($msg:tt)*) => {
493 $crate::Error::new($status, format!($($msg)*))
494 };
495}
496
497#[doc(hidden)]
498#[macro_export]
499macro_rules! check_status {
500 ($code:expr) => {{
501 let c = $code;
502 match c {
503 $crate::sys::Status::napi_ok => Ok(()),
504 _ => Err($crate::Error::new($crate::Status::from(c), "".to_owned())),
505 }
506 }};
507
508 ($code:expr, $($msg:tt)*) => {{
509 let c = $code;
510 match c {
511 $crate::sys::Status::napi_ok => Ok(()),
512 _ => Err($crate::Error::new($crate::Status::from(c), format!($($msg)*))),
513 }
514 }};
515
516 ($code:expr, $msg:expr, $env:expr, $val:expr) => {{
517 let c = $code;
518 match c {
519 $crate::sys::Status::napi_ok => Ok(()),
520 _ => Err($crate::Error::new($crate::Status::from(c), format!($msg, $crate::type_of!($env, $val)?))),
521 }
522 }};
523}
524
525#[doc(hidden)]
526#[macro_export]
527macro_rules! check_status_and_type {
528 ($code:expr, $env:ident, $val:ident, $msg:expr) => {{
529 let c = $code;
530 match c {
531 $crate::sys::Status::napi_ok => Ok(()),
532 _ => {
533 use $crate::js_values::JsValue;
534 let value_type = $crate::type_of!($env, $val)?;
535 let error_msg = match value_type {
536 ValueType::Function => {
537 let function_name = unsafe {
538 $crate::bindgen_prelude::Function::<
539 $crate::bindgen_prelude::Unknown,
540 $crate::bindgen_prelude::Unknown,
541 >::from_napi_value($env, $val)?
542 .name()?
543 };
544 format!(
545 $msg,
546 format!(
547 "function {}(..) ",
548 if function_name.len() == 0 {
549 "anonymous".to_owned()
550 } else {
551 function_name
552 }
553 )
554 )
555 }
556 ValueType::Object => {
557 let env_ = $crate::Env::from($env);
558 let json: $crate::JSON = env_.get_global()?.get_named_property_unchecked("JSON")?;
559 let object = json.stringify($crate::bindgen_prelude::Object::from_raw($env, $val))?;
560 format!($msg, format!("Object {}", object))
561 }
562 ValueType::Boolean | ValueType::Number => {
563 let val = $crate::Unknown::from_raw_unchecked($env, $val);
564 let value = val.coerce_to_string()?.into_utf8()?;
565 format!($msg, format!("{} {} ", value_type, value.as_str()?))
566 }
567 #[cfg(feature = "napi6")]
568 ValueType::BigInt => {
569 let val = $crate::Unknown::from_raw_unchecked($env, $val);
570 let value = val.coerce_to_string()?.into_utf8()?;
571 format!($msg, format!("{} {} ", value_type, value.as_str()?))
572 }
573 _ => format!($msg, value_type),
574 };
575 Err($crate::Error::new($crate::Status::from(c), error_msg))
576 }
577 }
578 }};
579}
580
581#[doc(hidden)]
582#[macro_export]
583macro_rules! check_pending_exception {
584 ($env:expr, $code:expr) => {{
585 let c = $code;
586 match c {
587 $crate::sys::Status::napi_ok => Ok(()),
588 $crate::sys::Status::napi_pending_exception => {
589 let mut error_result = std::ptr::null_mut();
590 assert_eq!(
591 unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
592 $crate::sys::Status::napi_ok
593 );
594 return Err($crate::Error::from(unsafe {
595 $crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
596 }));
597 }
598 _ => Err($crate::Error::new($crate::Status::from(c), "".to_owned())),
599 }
600 }};
601
602 ($env:expr, $code:expr, $($msg:tt)*) => {{
603 let c = $code;
604 match c {
605 $crate::sys::Status::napi_ok => Ok(()),
606 $crate::sys::Status::napi_pending_exception => {
607 let mut error_result = std::ptr::null_mut();
608 assert_eq!(
609 unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
610 $crate::sys::Status::napi_ok
611 );
612 return Err($crate::Error::from(unsafe {
613 $crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
614 }));
615 }
616 _ => Err($crate::Error::new($crate::Status::from(c), format!($($msg)*))),
617 }
618 }};
619}