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