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
15use crate::bindgen_runtime::JsObjectValue;
16#[cfg(target_family = "wasm")]
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: Option<Box<Error>> = value
150 .coerce_to_object()
151 .and_then(|obj| obj.get_named_property::<Unknown>("cause"))
152 .map(|cause| Box::new(cause.into()))
153 .ok();
154
155 if let Ok(error_message) = maybe_error_message {
156 return Self {
157 status: Status::GenericFailure,
158 reason: error_message,
159 cause: maybe_cause,
160 maybe_raw: result,
161 maybe_env,
162 };
163 }
164
165 Self {
166 status: Status::GenericFailure,
167 reason: "".to_string(),
168 cause: maybe_cause,
169 maybe_raw: result,
170 maybe_env,
171 }
172 }
173}
174
175#[cfg(target_family = "wasm")]
176impl From<Unknown<'_>> for Error {
177 fn from(value: Unknown) -> Self {
178 let value_type = value.get_type();
179
180 let maybe_error_message;
181
182 if let Ok(vt) = value_type {
183 if vt == ValueType::Object {
184 maybe_error_message = value
185 .coerce_to_object()
186 .and_then(|obj| obj.get_named_property::<Unknown>("message"))
187 .and_then(|message| {
188 message
189 .coerce_to_string()
190 .and_then(|message| message.into_utf8().and_then(|message| message.into_owned()))
191 });
192 } else {
193 maybe_error_message = value
194 .coerce_to_string()
195 .and_then(|a| a.into_utf8().and_then(|a| a.into_owned()));
196 }
197 } else {
198 maybe_error_message = value
199 .coerce_to_string()
200 .and_then(|a| a.into_utf8().and_then(|a| a.into_owned()));
201 };
202
203 let maybe_cause: Option<Box<Error>> = if let Ok(vt) = value_type {
204 if vt == ValueType::Object {
205 value
206 .coerce_to_object()
207 .and_then(|obj| obj.get_named_property::<Unknown>("cause"))
208 .map(|cause| Box::new(cause.into()))
209 .ok()
210 } else {
211 None
212 }
213 } else {
214 None
215 };
216
217 if let Ok(error_message) = maybe_error_message {
218 return Self {
219 status: Status::GenericFailure,
220 reason: error_message,
221 cause: maybe_cause,
222 maybe_raw: ptr::null_mut(),
223 maybe_env: ptr::null_mut(),
224 };
225 }
226
227 Self {
228 status: Status::GenericFailure,
229 reason: "".to_string(),
230 cause: maybe_cause,
231 maybe_raw: ptr::null_mut(),
232 maybe_env: ptr::null_mut(),
233 }
234 }
235}
236
237#[cfg(feature = "anyhow")]
238impl From<anyhow::Error> for Error {
239 fn from(value: anyhow::Error) -> Self {
240 Error::new(Status::GenericFailure, format!("{:?}", value))
241 }
242}
243
244impl<S: AsRef<str> + std::fmt::Debug> fmt::Display for Error<S> {
245 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
246 if !self.reason.is_empty() {
247 write!(f, "{:?}, {}", self.status, self.reason)
248 } else {
249 write!(f, "{:?}", self.status)
250 }
251 }
252}
253
254impl<S: AsRef<str>> Error<S> {
255 pub fn new<R: ToString>(status: S, reason: R) -> Self {
256 Error {
257 status,
258 reason: reason.to_string(),
259 cause: None,
260 maybe_raw: ptr::null_mut(),
261 maybe_env: ptr::null_mut(),
262 }
263 }
264
265 pub fn from_status(status: S) -> Self {
266 Error {
267 status,
268 reason: "".to_owned(),
269 cause: None,
270 maybe_raw: ptr::null_mut(),
271 maybe_env: ptr::null_mut(),
272 }
273 }
274}
275
276impl<S: AsRef<str> + Clone> Error<S> {
277 pub fn try_clone(&self) -> Result<Self> {
278 if !self.maybe_raw.is_null() {
279 check_status!(
280 unsafe { sys::napi_reference_ref(self.maybe_env, self.maybe_raw, &mut 0) },
281 "Failed to increase error reference count"
282 )?;
283 }
284 Ok(Self {
285 status: self.status.clone(),
286 reason: self.reason.to_string(),
287 cause: None,
288 maybe_raw: self.maybe_raw,
289 maybe_env: self.maybe_env,
290 })
291 }
292}
293
294impl Error {
295 pub fn from_reason<T: Into<String>>(reason: T) -> Self {
296 Error {
297 status: Status::GenericFailure,
298 reason: reason.into(),
299 cause: None,
300 maybe_raw: ptr::null_mut(),
301 maybe_env: ptr::null_mut(),
302 }
303 }
304}
305
306impl From<std::ffi::NulError> for Error {
307 fn from(error: std::ffi::NulError) -> Self {
308 Error {
309 status: Status::GenericFailure,
310 reason: format!("{error}"),
311 cause: None,
312 maybe_raw: ptr::null_mut(),
313 maybe_env: ptr::null_mut(),
314 }
315 }
316}
317
318impl From<std::io::Error> for Error {
319 fn from(error: std::io::Error) -> Self {
320 Error {
321 status: Status::GenericFailure,
322 reason: format!("{error}"),
323 cause: None,
324 maybe_raw: ptr::null_mut(),
325 maybe_env: ptr::null_mut(),
326 }
327 }
328}
329
330#[derive(Clone, Debug)]
331pub struct ExtendedErrorInfo {
332 pub message: String,
333 pub engine_reserved: *mut c_void,
334 pub engine_error_code: u32,
335 pub error_code: Status,
336}
337
338impl TryFrom<sys::napi_extended_error_info> for ExtendedErrorInfo {
339 type Error = Error;
340
341 fn try_from(value: sys::napi_extended_error_info) -> Result<Self> {
342 Ok(Self {
343 message: unsafe {
344 CStr::from_ptr(value.error_message.cast())
345 .to_str()
346 .map_err(|e| Error::new(Status::GenericFailure, format!("{e}")))?
347 .to_owned()
348 },
349 engine_error_code: value.engine_error_code,
350 engine_reserved: value.engine_reserved,
351 error_code: Status::from(value.error_code),
352 })
353 }
354}
355
356pub struct JsError<S: AsRef<str> = Status>(Error<S>);
357
358#[cfg(feature = "anyhow")]
359impl From<anyhow::Error> for JsError {
360 fn from(value: anyhow::Error) -> Self {
361 JsError(Error::new(Status::GenericFailure, value.to_string()))
362 }
363}
364
365pub struct JsTypeError<S: AsRef<str> = Status>(Error<S>);
366
367pub struct JsRangeError<S: AsRef<str> = Status>(Error<S>);
368
369#[cfg(feature = "napi9")]
370pub struct JsSyntaxError<S: AsRef<str> = Status>(Error<S>);
371
372pub(crate) fn get_error_message_and_stack_trace(
373 env: sys::napi_env,
374 err: sys::napi_value,
375) -> Result<String> {
376 use crate::bindgen_runtime::FromNapiValue;
377
378 let mut error_string = ptr::null_mut();
379 check_status!(
380 unsafe { sys::napi_coerce_to_string(env, err, &mut error_string) },
381 "Get error message failed"
382 )?;
383 let mut result = unsafe { String::from_napi_value(env, error_string) }?;
384
385 let mut stack_trace = ptr::null_mut();
386 check_status!(
387 unsafe { sys::napi_get_named_property(env, err, c"stack".as_ptr().cast(), &mut stack_trace) },
388 "Get stack trace failed"
389 )?;
390 let mut stack_type = -1;
391 check_status!(
392 unsafe { sys::napi_typeof(env, stack_trace, &mut stack_type) },
393 "Get stack trace type failed"
394 )?;
395 if stack_type == sys::ValueType::napi_string {
396 let stack_trace = unsafe { String::from_napi_value(env, stack_trace) }?;
397 result.push('\n');
398 result.push_str(&stack_trace);
399 }
400
401 Ok(result)
402}
403
404macro_rules! impl_object_methods {
405 ($js_value:ident, $kind:expr) => {
406 impl<S: AsRef<str>> $js_value<S> {
407 pub unsafe fn into_value(mut self, env: sys::napi_env) -> sys::napi_value {
411 if !self.0.maybe_raw.is_null() {
412 let mut err = ptr::null_mut();
413 let get_err_status =
414 unsafe { sys::napi_get_reference_value(env, self.0.maybe_raw, &mut err) };
415 debug_assert!(
416 get_err_status == sys::Status::napi_ok,
417 "Get Error from Reference failed"
418 );
419 let mut ref_count = 0;
420 let unref_status =
421 unsafe { sys::napi_reference_unref(env, self.0.maybe_raw, &mut ref_count) };
422 debug_assert!(
423 unref_status == sys::Status::napi_ok,
424 "Unref Error Reference failed"
425 );
426 if ref_count == 0 {
427 let delete_err_status = unsafe { sys::napi_delete_reference(env, self.0.maybe_raw) };
428 debug_assert!(
429 delete_err_status == sys::Status::napi_ok,
430 "Delete Error Reference failed"
431 );
432 }
433 self.0.maybe_raw = ptr::null_mut();
435 self.0.maybe_env = ptr::null_mut();
436 let mut is_error = false;
437 let is_error_status = unsafe { sys::napi_is_error(env, err, &mut is_error) };
438 debug_assert!(
439 is_error_status == sys::Status::napi_ok,
440 "Check Error failed"
441 );
442 if is_error {
444 return err;
445 }
446 }
447
448 let error_status = self.0.status.as_ref();
449 let status_len = error_status.len();
450 let reason_len = self.0.reason.len();
451 let mut error_code = ptr::null_mut();
452 let mut reason_string = ptr::null_mut();
453 let mut js_error = ptr::null_mut();
454 let create_code_status = unsafe {
455 sys::napi_create_string_utf8(
456 env,
457 error_status.as_ptr().cast(),
458 status_len as isize,
459 &mut error_code,
460 )
461 };
462 debug_assert!(create_code_status == sys::Status::napi_ok);
463 let create_reason_status = unsafe {
464 sys::napi_create_string_utf8(
465 env,
466 self.0.reason.as_ptr().cast(),
467 reason_len as isize,
468 &mut reason_string,
469 )
470 };
471 debug_assert!(create_reason_status == sys::Status::napi_ok);
472 let create_error_status = unsafe { $kind(env, error_code, reason_string, &mut js_error) };
473 debug_assert!(create_error_status == sys::Status::napi_ok);
474 if let Some(cause_error) = self.0.cause.take() {
475 let cause = ToNapiValue::to_napi_value(env, *cause_error)
476 .expect("Convert cause Error to napi_value should never error");
477 let set_cause_status =
478 unsafe { sys::napi_set_named_property(env, js_error, c"cause".as_ptr().cast(), cause) };
479 debug_assert!(
480 set_cause_status == sys::Status::napi_ok,
481 "Set cause property failed"
482 );
483 }
484 js_error
485 }
486
487 pub fn into_unknown<'env>(self, env: Env) -> Unknown<'env> {
488 let value = unsafe { self.into_value(env.raw()) };
489 unsafe { Unknown::from_raw_unchecked(env.raw(), value) }
490 }
491
492 pub unsafe fn throw_into(self, env: sys::napi_env) {
496 #[cfg(debug_assertions)]
497 let reason = self.0.reason.clone();
498 let status = self.0.status.as_ref().to_string();
499 if status == Status::PendingException.as_ref() {
501 return;
502 }
503 let mut is_pending_exception = false;
505 assert_eq!(
506 unsafe { $crate::sys::napi_is_exception_pending(env, &mut is_pending_exception) },
507 $crate::sys::Status::napi_ok,
508 "Check exception status failed"
509 );
510 let js_error = match is_pending_exception {
511 true => {
512 let mut error_result = std::ptr::null_mut();
513 assert_eq!(
514 unsafe { $crate::sys::napi_get_and_clear_last_exception(env, &mut error_result) },
515 $crate::sys::Status::napi_ok,
516 "Get and clear last exception failed"
517 );
518 error_result
519 }
520 false => unsafe { self.into_value(env) },
521 };
522 #[cfg(debug_assertions)]
523 let throw_status = unsafe { sys::napi_throw(env, js_error) };
524 unsafe { sys::napi_throw(env, js_error) };
525 #[cfg(debug_assertions)]
526 assert!(
527 throw_status == sys::Status::napi_ok,
528 "Throw error failed, status: [{}], raw message: \"{}\", raw status: [{}]",
529 Status::from(throw_status),
530 reason,
531 status
532 );
533 }
534 }
535
536 impl<S: AsRef<str>> From<Error<S>> for $js_value<S> {
537 fn from(err: Error<S>) -> Self {
538 Self(err)
539 }
540 }
541
542 impl crate::bindgen_prelude::ToNapiValue for $js_value {
543 unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
544 unsafe { ToNapiValue::to_napi_value(env, val.0) }
545 }
546 }
547 };
548}
549
550impl_object_methods!(JsError, sys::napi_create_error);
551impl_object_methods!(JsTypeError, sys::napi_create_type_error);
552impl_object_methods!(JsRangeError, sys::napi_create_range_error);
553#[cfg(feature = "napi9")]
554impl_object_methods!(JsSyntaxError, sys::node_api_create_syntax_error);
555
556#[doc(hidden)]
557#[macro_export]
558macro_rules! error {
559 ($status:expr, $($msg:tt)*) => {
560 $crate::Error::new($status, format!($($msg)*))
561 };
562}
563
564#[doc(hidden)]
565#[macro_export]
566macro_rules! check_status {
567 ($code:expr) => {{
568 let c = $code;
569 match c {
570 $crate::sys::Status::napi_ok => Ok(()),
571 _ => Err($crate::Error::new($crate::Status::from(c), "".to_owned())),
572 }
573 }};
574
575 ($code:expr, $($msg:tt)*) => {{
576 let c = $code;
577 match c {
578 $crate::sys::Status::napi_ok => Ok(()),
579 _ => Err($crate::Error::new($crate::Status::from(c), format!($($msg)*))),
580 }
581 }};
582
583 ($code:expr, $msg:expr, $env:expr, $val:expr) => {{
584 let c = $code;
585 match c {
586 $crate::sys::Status::napi_ok => Ok(()),
587 _ => Err($crate::Error::new($crate::Status::from(c), format!($msg, $crate::type_of!($env, $val)?))),
588 }
589 }};
590}
591
592#[doc(hidden)]
593#[macro_export]
594macro_rules! check_status_and_type {
595 ($code:expr, $env:ident, $val:ident, $msg:expr) => {{
596 let c = $code;
597 match c {
598 $crate::sys::Status::napi_ok => Ok(()),
599 _ => {
600 use $crate::js_values::JsValue;
601 let value_type = $crate::type_of!($env, $val)?;
602 let error_msg = match value_type {
603 ValueType::Function => {
604 let function_name = unsafe {
605 $crate::bindgen_prelude::Function::<
606 $crate::bindgen_prelude::Unknown,
607 $crate::bindgen_prelude::Unknown,
608 >::from_napi_value($env, $val)?
609 .name()?
610 };
611 format!(
612 $msg,
613 format!(
614 "function {}(..) ",
615 if function_name.len() == 0 {
616 "anonymous".to_owned()
617 } else {
618 function_name
619 }
620 )
621 )
622 }
623 ValueType::Object => {
624 let env_ = $crate::Env::from($env);
625 let json: $crate::JSON = env_.get_global()?.get_named_property_unchecked("JSON")?;
626 let object = json.stringify($crate::bindgen_prelude::Object::from_raw($env, $val))?;
627 format!($msg, format!("Object {}", object))
628 }
629 ValueType::Boolean | ValueType::Number => {
630 let val = $crate::Unknown::from_raw_unchecked($env, $val);
631 let value = val.coerce_to_string()?.into_utf8()?;
632 format!($msg, format!("{} {} ", value_type, value.as_str()?))
633 }
634 #[cfg(feature = "napi6")]
635 ValueType::BigInt => {
636 let val = $crate::Unknown::from_raw_unchecked($env, $val);
637 let value = val.coerce_to_string()?.into_utf8()?;
638 format!($msg, format!("{} {} ", value_type, value.as_str()?))
639 }
640 _ => format!($msg, value_type),
641 };
642 Err($crate::Error::new($crate::Status::from(c), error_msg))
643 }
644 }
645 }};
646}
647
648#[doc(hidden)]
649#[macro_export]
650macro_rules! check_pending_exception {
651 ($env:expr, $code:expr) => {{
652 let c = $code;
653 match c {
654 $crate::sys::Status::napi_ok => Ok(()),
655 $crate::sys::Status::napi_pending_exception => {
656 let mut error_result = std::ptr::null_mut();
657 assert_eq!(
658 unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
659 $crate::sys::Status::napi_ok
660 );
661 return Err($crate::Error::from(unsafe {
662 $crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
663 }));
664 }
665 _ => Err($crate::Error::new($crate::Status::from(c), "".to_owned())),
666 }
667 }};
668
669 ($env:expr, $code:expr, $($msg:tt)*) => {{
670 let c = $code;
671 match c {
672 $crate::sys::Status::napi_ok => Ok(()),
673 $crate::sys::Status::napi_pending_exception => {
674 let mut error_result = std::ptr::null_mut();
675 assert_eq!(
676 unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
677 $crate::sys::Status::napi_ok
678 );
679 return Err($crate::Error::from(unsafe {
680 $crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
681 }));
682 }
683 _ => Err($crate::Error::new($crate::Status::from(c), format!($($msg)*))),
684 }
685 }};
686}