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: *mut napi_value__ = unsafe { JsError::from(val).into_value(env) };
35 Ok(err)
36 } else {
37 let mut value: *mut napi_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: IntoStringError| Error::new(Status::GenericFailure, reason: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}
480