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