napi_h/
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          return err;
240        }
241
242        let error_status = self.0.status.as_ref();
243        let status_len = error_status.len();
244        let error_code_string = CString::new(error_status).unwrap();
245        let reason_len = self.0.reason.len();
246        let reason = CString::new(self.0.reason.as_str()).unwrap();
247        let mut error_code = ptr::null_mut();
248        let mut reason_string = ptr::null_mut();
249        let mut js_error = ptr::null_mut();
250        let create_code_status = unsafe {
251          sys::napi_create_string_utf8(env, error_code_string.as_ptr(), status_len, &mut error_code)
252        };
253        debug_assert!(create_code_status == sys::Status::napi_ok);
254        let create_reason_status = unsafe {
255          sys::napi_create_string_utf8(env, reason.as_ptr(), reason_len, &mut reason_string)
256        };
257        debug_assert!(create_reason_status == sys::Status::napi_ok);
258        let create_error_status = unsafe { $kind(env, error_code, reason_string, &mut js_error) };
259        debug_assert!(create_error_status == sys::Status::napi_ok);
260        js_error
261      }
262
263      pub fn into_unknown(self, env: Env) -> JsUnknown {
264        let value = unsafe { self.into_value(env.raw()) };
265        unsafe { JsUnknown::from_raw_unchecked(env.raw(), value) }
266      }
267
268      /// # Safety
269      ///
270      /// This function is safety if env is not null ptr.
271      pub unsafe fn throw_into(self, env: sys::napi_env) {
272        #[cfg(debug_assertions)]
273        let reason = self.0.reason.clone();
274        let status = self.0.status.as_ref().to_string();
275        if status == Status::PendingException.as_ref() {
276          return;
277        }
278        let js_error = unsafe { self.into_value(env) };
279        #[cfg(debug_assertions)]
280        let throw_status = unsafe { sys::napi_throw(env, js_error) };
281        unsafe { sys::napi_throw(env, js_error) };
282        #[cfg(debug_assertions)]
283        assert!(
284          throw_status == sys::Status::napi_ok,
285          "Throw error failed, status: [{}], raw message: \"{}\", raw status: [{}]",
286          Status::from(throw_status),
287          reason,
288          status
289        );
290      }
291    }
292
293    impl<S: AsRef<str>> From<Error<S>> for $js_value<S> {
294      fn from(err: Error<S>) -> Self {
295        Self(err)
296      }
297    }
298
299    impl crate::bindgen_prelude::ToNapiValue for $js_value {
300      unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result<sys::napi_value> {
301        unsafe { ToNapiValue::to_napi_value(env, val.0) }
302      }
303    }
304  };
305}
306
307impl_object_methods!(JsError, sys::napi_create_error);
308impl_object_methods!(JsTypeError, sys::napi_create_type_error);
309impl_object_methods!(JsRangeError, sys::napi_create_range_error);
310#[cfg(feature = "napi9")]
311impl_object_methods!(JsSyntaxError, sys::node_api_create_syntax_error);
312
313#[doc(hidden)]
314#[macro_export]
315macro_rules! error {
316  ($status:expr, $($msg:tt)*) => {
317    $crate::Error::new($status, format!($($msg)*))
318  };
319}
320
321#[doc(hidden)]
322#[macro_export]
323macro_rules! check_status {
324  ($code:expr) => {{
325    let c = $code;
326    match c {
327      $crate::sys::Status::napi_ok => Ok(()),
328      _ => Err($crate::Error::new($crate::Status::from(c), "".to_owned())),
329    }
330  }};
331
332  ($code:expr, $($msg:tt)*) => {{
333    let c = $code;
334    match c {
335      $crate::sys::Status::napi_ok => Ok(()),
336      _ => Err($crate::Error::new($crate::Status::from(c), format!($($msg)*))),
337    }
338  }};
339
340  ($code:expr, $msg:expr, $env:expr, $val:expr) => {{
341    let c = $code;
342    match c {
343      $crate::sys::Status::napi_ok => Ok(()),
344      _ => Err($crate::Error::new($crate::Status::from(c), format!($msg, $crate::type_of!($env, $val)?))),
345    }
346  }};
347}
348
349#[doc(hidden)]
350#[macro_export]
351macro_rules! check_status_and_type {
352  ($code:expr, $env:ident, $val:ident, $msg:expr) => {{
353    let c = $code;
354    match c {
355      $crate::sys::Status::napi_ok => Ok(()),
356      _ => {
357        use $crate::js_values::NapiValue;
358        let value_type = $crate::type_of!($env, $val)?;
359        let error_msg = match value_type {
360          ValueType::Function => {
361            let function_name = unsafe { JsFunction::from_raw_unchecked($env, $val).name()? };
362            format!(
363              $msg,
364              format!(
365                "function {}(..) ",
366                if function_name.len() == 0 {
367                  "anonymous".to_owned()
368                } else {
369                  function_name
370                }
371              )
372            )
373          }
374          ValueType::Object => {
375            let env_ = $crate::Env::from($env);
376            let json: $crate::JSON = env_.get_global()?.get_named_property_unchecked("JSON")?;
377            let object = json.stringify($crate::JsObject($crate::Value {
378              value: $val,
379              env: $env,
380              value_type: ValueType::Object,
381            }))?;
382            format!($msg, format!("Object {}", object))
383          }
384          ValueType::Boolean | ValueType::Number => {
385            let value =
386              unsafe { $crate::JsUnknown::from_raw_unchecked($env, $val).coerce_to_string()? }
387                .into_utf8()?;
388            format!($msg, format!("{} {} ", value_type, value.as_str()?))
389          }
390          #[cfg(feature = "napi6")]
391          ValueType::BigInt => {
392            let value =
393              unsafe { $crate::JsUnknown::from_raw_unchecked($env, $val).coerce_to_string()? }
394                .into_utf8()?;
395            format!($msg, format!("{} {} ", value_type, value.as_str()?))
396          }
397          _ => format!($msg, value_type),
398        };
399        Err($crate::Error::new($crate::Status::from(c), error_msg))
400      }
401    }
402  }};
403}
404
405#[doc(hidden)]
406#[macro_export]
407macro_rules! check_pending_exception {
408  ($env:expr, $code:expr) => {{
409    use $crate::NapiValue;
410    let c = $code;
411    match c {
412      $crate::sys::Status::napi_ok => Ok(()),
413      $crate::sys::Status::napi_pending_exception => {
414        let mut error_result = std::ptr::null_mut();
415        assert_eq!(
416          unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
417          $crate::sys::Status::napi_ok
418        );
419        return Err($crate::Error::from(unsafe {
420          $crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
421        }));
422      }
423      _ => Err($crate::Error::new($crate::Status::from(c), "".to_owned())),
424    }
425  }};
426
427  ($env:expr, $code:expr, $($msg:tt)*) => {{
428    use $crate::NapiValue;
429    let c = $code;
430    match c {
431      $crate::sys::Status::napi_ok => Ok(()),
432      $crate::sys::Status::napi_pending_exception => {
433        let mut error_result = std::ptr::null_mut();
434        assert_eq!(
435          unsafe { $crate::sys::napi_get_and_clear_last_exception($env, &mut error_result) },
436          $crate::sys::Status::napi_ok
437        );
438        return Err($crate::Error::from(unsafe {
439          $crate::bindgen_prelude::Unknown::from_raw_unchecked($env, error_result)
440        }));
441      }
442      _ => Err($crate::Error::new($crate::Status::from(c), format!($($msg)*))),
443    }
444  }};
445}