napi_calm_down/
error.rs

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;
16use crate::{check_status, sys, Env, JsUnknown, NapiValue, Status};
17
18pub type Result<T, S = Status> = std::result::Result<T, Error<S>>;
19
20/// Represent `JsError`.
21/// Return this Error in `js_function`, **napi-rs** will throw it as `JsError` for you.
22/// If you want throw it as `TypeError` or `RangeError`, you can use `JsTypeError/JsRangeError::from(Error).throw_into(env)`
23#[derive(Debug, Clone)]
24pub struct Error<S: AsRef<str> = Status> {
25  pub status: S,
26  pub reason: String,
27  // Convert raw `JsError` into Error
28  pub(crate) maybe_raw: sys::napi_ref,
29}
30
31impl<S: AsRef<str>> ToNapiValue for Error<S> {
32  unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
33    if val.maybe_raw.is_null() {
34      let err = unsafe { JsError::from(val).into_value(env) };
35      Ok(err)
36    } else {
37      let mut value = std::ptr::null_mut();
38      check_status!(
39        unsafe { sys::napi_get_reference_value(env, val.maybe_raw, &mut value) },
40        "Get error reference in `to_napi_value` failed"
41      )?;
42      check_status!(
43        unsafe { sys::napi_delete_reference(env, val.maybe_raw) },
44        "Delete error reference in `to_napi_value` failed"
45      )?;
46      Ok(value)
47    }
48  }
49}
50
51unsafe impl<S> Send for Error<S> where S: Send + AsRef<str> {}
52unsafe impl<S> Sync for Error<S> where S: Sync + AsRef<str> {}
53
54impl<S: AsRef<str> + std::fmt::Debug> error::Error for Error<S> {}
55
56impl<S: AsRef<str>> From<std::convert::Infallible> for Error<S> {
57  fn from(_: std::convert::Infallible) -> Self {
58    unreachable!()
59  }
60}
61
62#[cfg(feature = "serde-json")]
63impl ser::Error for Error {
64  fn custom<T: Display>(msg: T) -> Self {
65    Error::new(Status::InvalidArg, msg.to_string())
66  }
67}
68
69#[cfg(feature = "serde-json")]
70impl de::Error for Error {
71  fn custom<T: Display>(msg: T) -> Self {
72    Error::new(Status::InvalidArg, msg.to_string())
73  }
74}
75
76#[cfg(feature = "serde-json")]
77impl From<SerdeJSONError> for Error {
78  fn from(value: SerdeJSONError) -> Self {
79    Error::new(Status::InvalidArg, format!("{}", value))
80  }
81}
82
83impl From<JsUnknown> for Error {
84  fn from(value: JsUnknown) -> Self {
85    let mut result = std::ptr::null_mut();
86    let status = unsafe { sys::napi_create_reference(value.0.env, value.0.value, 1, &mut result) };
87    if status != sys::Status::napi_ok {
88      return Error::new(
89        Status::from(status),
90        "Create Error reference failed".to_owned(),
91      );
92    }
93
94    let maybe_error_message = value
95      .coerce_to_string()
96      .and_then(|a| a.into_utf8().and_then(|a| a.into_owned()));
97    if let Ok(error_message) = maybe_error_message {
98      return Self {
99        status: Status::GenericFailure,
100        reason: error_message,
101        maybe_raw: result,
102      };
103    }
104
105    Self {
106      status: Status::GenericFailure,
107      reason: "".to_string(),
108      maybe_raw: result,
109    }
110  }
111}
112
113#[cfg(feature = "anyhow")]
114impl From<anyhow::Error> for Error {
115  fn from(value: anyhow::Error) -> Self {
116    Error::new(Status::GenericFailure, format!("{:?}", value))
117  }
118}
119
120impl<S: AsRef<str> + std::fmt::Debug> fmt::Display for Error<S> {
121  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
122    if !self.reason.is_empty() {
123      write!(f, "{:?}, {}", self.status, self.reason)
124    } else {
125      write!(f, "{:?}", self.status)
126    }
127  }
128}
129
130impl<S: AsRef<str>> Error<S> {
131  pub fn new<R: ToString>(status: S, reason: R) -> Self {
132    Error {
133      status,
134      reason: reason.to_string(),
135      maybe_raw: ptr::null_mut(),
136    }
137  }
138
139  pub fn from_status(status: S) -> Self {
140    Error {
141      status,
142      reason: "".to_owned(),
143      maybe_raw: ptr::null_mut(),
144    }
145  }
146}
147
148impl Error {
149  pub fn from_reason<T: Into<String>>(reason: T) -> Self {
150    Error {
151      status: Status::GenericFailure,
152      reason: reason.into(),
153      maybe_raw: ptr::null_mut(),
154    }
155  }
156}
157
158impl From<std::ffi::NulError> for Error {
159  fn from(error: std::ffi::NulError) -> Self {
160    Error {
161      status: Status::GenericFailure,
162      reason: format!("{}", error),
163      maybe_raw: ptr::null_mut(),
164    }
165  }
166}
167
168impl From<std::io::Error> for Error {
169  fn from(error: std::io::Error) -> Self {
170    Error {
171      status: Status::GenericFailure,
172      reason: format!("{}", error),
173      maybe_raw: ptr::null_mut(),
174    }
175  }
176}
177
178#[derive(Clone, Debug)]
179pub struct ExtendedErrorInfo {
180  pub message: String,
181  pub engine_reserved: *mut c_void,
182  pub engine_error_code: u32,
183  pub error_code: Status,
184}
185
186impl TryFrom<sys::napi_extended_error_info> for ExtendedErrorInfo {
187  type Error = Error;
188
189  fn try_from(value: sys::napi_extended_error_info) -> Result<Self> {
190    Ok(Self {
191      message: unsafe {
192        CString::from_raw(value.error_message as *mut c_char)
193          .into_string()
194          .map_err(|e| Error::new(Status::GenericFailure, format!("{}", e)))?
195      },
196      engine_error_code: value.engine_error_code,
197      engine_reserved: value.engine_reserved,
198      error_code: Status::from(value.error_code),
199    })
200  }
201}
202
203pub struct JsError<S: AsRef<str> = Status>(Error<S>);
204
205#[cfg(feature = "anyhow")]
206impl From<anyhow::Error> for JsError {
207  fn from(value: anyhow::Error) -> Self {
208    JsError(Error::new(Status::GenericFailure, value.to_string()))
209  }
210}
211
212pub struct JsTypeError<S: AsRef<str> = Status>(Error<S>);
213
214pub struct JsRangeError<S: AsRef<str> = Status>(Error<S>);
215
216#[cfg(feature = "napi9")]
217pub struct JsSyntaxError<S: AsRef<str> = Status>(Error<S>);
218
219macro_rules! impl_object_methods {
220  ($js_value:ident, $kind:expr) => {
221    impl<S: AsRef<str>> $js_value<S> {
222      /// # Safety
223      ///
224      /// This function is safety if env is not null ptr.
225      pub unsafe fn into_value(self, env: sys::napi_env) -> sys::napi_value {
226        if !self.0.maybe_raw.is_null() {
227          let mut err = ptr::null_mut();
228          let get_err_status =
229            unsafe { sys::napi_get_reference_value(env, self.0.maybe_raw, &mut err) };
230          debug_assert!(
231            get_err_status == sys::Status::napi_ok,
232            "Get Error from Reference failed"
233          );
234          let delete_err_status = unsafe { sys::napi_delete_reference(env, self.0.maybe_raw) };
235          debug_assert!(
236            delete_err_status == sys::Status::napi_ok,
237            "Delete Error Reference failed"
238          );
239          let mut is_error = false;
240          let is_error_status = unsafe { sys::napi_is_error(env, err, &mut is_error) };
241          debug_assert!(
242            is_error_status == sys::Status::napi_ok,
243            "Check Error failed"
244          );
245          // make sure ref_value is a valid error at first and avoid throw error failed.
246          if is_error {
247            return err;
248          }
249        }
250
251        let error_status = self.0.status.as_ref();
252        let status_len = error_status.len();
253        let reason_len = self.0.reason.len();
254        let mut error_code = ptr::null_mut();
255        let mut reason_string = ptr::null_mut();
256        let mut js_error = ptr::null_mut();
257        let create_code_status = unsafe {
258          sys::napi_create_string_utf8(
259            env,
260            error_status.as_ptr().cast(),
261            status_len,
262            &mut error_code,
263          )
264        };
265        debug_assert!(create_code_status == sys::Status::napi_ok);
266        let create_reason_status = unsafe {
267          sys::napi_create_string_utf8(
268            env,
269            self.0.reason.as_ptr().cast(),
270            reason_len,
271            &mut reason_string,
272          )
273        };
274        debug_assert!(create_reason_status == sys::Status::napi_ok);
275        let create_error_status = unsafe { $kind(env, error_code, reason_string, &mut js_error) };
276        debug_assert!(create_error_status == sys::Status::napi_ok);
277        js_error
278      }
279
280      pub fn into_unknown(self, env: Env) -> JsUnknown {
281        let value = unsafe { self.into_value(env.raw()) };
282        unsafe { JsUnknown::from_raw_unchecked(env.raw(), value) }
283      }
284
285      /// # Safety
286      ///
287      /// This function is safety if env is not null ptr.
288      pub unsafe fn throw_into(self, env: sys::napi_env) {
289        #[cfg(debug_assertions)]
290        let reason = self.0.reason.clone();
291        let status = self.0.status.as_ref().to_string();
292        // just sure current error is pending_exception
293        if status == Status::PendingException.as_ref() {
294          return;
295        }
296        // make sure current env is not exception_pending status
297        let mut is_pending_exception = false;
298        assert_eq!(
299          unsafe { $crate::sys::napi_is_exception_pending(env, &mut is_pending_exception) },
300          $crate::sys::Status::napi_ok
301        );
302        let js_error = match is_pending_exception {
303          true => {
304            let mut error_result = std::ptr::null_mut();
305            assert_eq!(
306              unsafe { $crate::sys::napi_get_and_clear_last_exception(env, &mut error_result) },
307              $crate::sys::Status::napi_ok
308            );
309            error_result
310          }
311          false => unsafe { self.into_value(env) },
312        };
313        #[cfg(debug_assertions)]
314        let throw_status = unsafe { sys::napi_throw(env, js_error) };
315        unsafe { sys::napi_throw(env, js_error) };
316        #[cfg(debug_assertions)]
317        assert!(
318          throw_status == sys::Status::napi_ok,
319          "Throw error failed, status: [{}], raw message: \"{}\", raw status: [{}]",
320          Status::from(throw_status),
321          reason,
322          status
323        );
324      }
325    }
326
327    impl<S: AsRef<str>> From<Error<S>> for $js_value<S> {
328      fn from(err: Error<S>) -> Self {
329        Self(err)
330      }
331    }
332
333    impl crate::bindgen_prelude::ToNapiValue for $js_value {
334      unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
335        unsafe { ToNapiValue::to_napi_value(env, val.0) }
336      }
337    }
338  };
339}
340
341impl_object_methods!(JsError, sys::napi_create_error);
342impl_object_methods!(JsTypeError, sys::napi_create_type_error);
343impl_object_methods!(JsRangeError, sys::napi_create_range_error);
344#[cfg(feature = "napi9")]
345impl_object_methods!(JsSyntaxError, sys::node_api_create_syntax_error);
346
347#[doc(hidden)]
348#[macro_export]
349macro_rules! error {
350  ($status:expr, $($msg:tt)*) => {
351    $crate::Error::new($status, format!($($msg)*))
352  };
353}
354
355#[doc(hidden)]
356#[macro_export]
357macro_rules! check_status {
358  ($code:expr) => {{
359    let c = $code;
360    match c {
361      $crate::sys::Status::napi_ok => Ok(()),
362      _ => Err($crate::Error::new($crate::Status::from(c), "".to_owned())),
363    }
364  }};
365
366  ($code:expr, $($msg:tt)*) => {{
367    let c = $code;
368    match c {
369      $crate::sys::Status::napi_ok => Ok(()),
370      _ => Err($crate::Error::new($crate::Status::from(c), format!($($msg)*))),
371    }
372  }};
373
374  ($code:expr, $msg:expr, $env:expr, $val:expr) => {{
375    let c = $code;
376    match c {
377      $crate::sys::Status::napi_ok => Ok(()),
378      _ => Err($crate::Error::new($crate::Status::from(c), format!($msg, $crate::type_of!($env, $val)?))),
379    }
380  }};
381}
382
383#[doc(hidden)]
384#[macro_export]
385macro_rules! check_status_and_type {
386  ($code:expr, $env:ident, $val:ident, $msg:expr) => {{
387    let c = $code;
388    match c {
389      $crate::sys::Status::napi_ok => Ok(()),
390      _ => {
391        use $crate::js_values::NapiValue;
392        let value_type = $crate::type_of!($env, $val)?;
393        let error_msg = match value_type {
394          ValueType::Function => {
395            let function_name = unsafe { JsFunction::from_raw_unchecked($env, $val).name()? };
396            format!(
397              $msg,
398              format!(
399                "function {}(..) ",
400                if function_name.len() == 0 {
401                  "anonymous".to_owned()
402                } else {
403                  function_name
404                }
405              )
406            )
407          }
408          ValueType::Object => {
409            let env_ = $crate::Env::from($env);
410            let json: $crate::JSON = env_.get_global()?.get_named_property_unchecked("JSON")?;
411            let object = json.stringify($crate::JsObject($crate::Value {
412              value: $val,
413              env: $env,
414              value_type: ValueType::Object,
415            }))?;
416            format!($msg, format!("Object {}", object))
417          }
418          ValueType::Boolean | ValueType::Number => {
419            let value =
420              unsafe { $crate::JsUnknown::from_raw_unchecked($env, $val).coerce_to_string()? }
421                .into_utf8()?;
422            format!($msg, format!("{} {} ", value_type, value.as_str()?))
423          }
424          #[cfg(feature = "napi6")]
425          ValueType::BigInt => {
426            let value =
427              unsafe { $crate::JsUnknown::from_raw_unchecked($env, $val).coerce_to_string()? }
428                .into_utf8()?;
429            format!($msg, format!("{} {} ", value_type, value.as_str()?))
430          }
431          _ => format!($msg, value_type),
432        };
433        Err($crate::Error::new($crate::Status::from(c), error_msg))
434      }
435    }
436  }};
437}
438
439#[doc(hidden)]
440#[macro_export]
441macro_rules! check_pending_exception {
442  ($env:expr, $code:expr) => {{
443    use $crate::NapiValue;
444    let c = $code;
445    match c {
446      $crate::sys::Status::napi_ok => Ok(()),
447      $crate::sys::Status::napi_pending_exception => {
448        let mut error_result = std::ptr::null_mut();
449        assert_eq!(
450          unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
451          $crate::sys::Status::napi_ok
452        );
453        return Err($crate::Error::from(unsafe {
454          $crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
455        }));
456      }
457      _ => Err($crate::Error::new($crate::Status::from(c), "".to_owned())),
458    }
459  }};
460
461  ($env:expr, $code:expr, $($msg:tt)*) => {{
462    use $crate::NapiValue;
463    let c = $code;
464    match c {
465      $crate::sys::Status::napi_ok => Ok(()),
466      $crate::sys::Status::napi_pending_exception => {
467        let mut error_result = std::ptr::null_mut();
468        assert_eq!(
469          unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
470          $crate::sys::Status::napi_ok
471        );
472        return Err($crate::Error::from(unsafe {
473          $crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
474        }));
475      }
476      _ => Err($crate::Error::new($crate::Status::from(c), format!($($msg)*))),
477    }
478  }};
479}