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 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}
446