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