1#![deny(clippy::unnecessary_wraps)]
3#![deny(clippy::print_stderr)]
4#![deny(clippy::print_stdout)]
5
6mod error_codes;
160
161pub use deno_error_macro::*;
162pub use error_codes::*;
163use std::any::Any;
164use std::borrow::Cow;
165
166pub mod builtin_classes {
169 pub const GENERIC_ERROR: &str = "Error";
171 pub const RANGE_ERROR: &str = "RangeError";
172 pub const TYPE_ERROR: &str = "TypeError";
173 pub const SYNTAX_ERROR: &str = "SyntaxError";
174 pub const URI_ERROR: &str = "URIError";
175 pub const REFERENCE_ERROR: &str = "ReferenceError";
176
177 pub const NOT_SUPPORTED_ERROR: &str = "NotSupported";
179}
180use builtin_classes::*;
181
182#[derive(Debug, Clone, PartialEq)]
184pub enum PropertyValue {
185 String(Cow<'static, str>),
186 Number(f64),
187}
188
189impl std::fmt::Display for PropertyValue {
190 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
191 match self {
192 PropertyValue::String(s) => write!(f, "{}", s),
193 PropertyValue::Number(n) => write!(f, "{}", n),
194 }
195 }
196}
197
198impl From<String> for PropertyValue {
199 fn from(s: String) -> Self {
200 PropertyValue::String(Cow::Owned(s))
201 }
202}
203
204impl From<&'static str> for PropertyValue {
205 fn from(s: &'static str) -> Self {
206 PropertyValue::String(Cow::Borrowed(s))
207 }
208}
209
210impl From<f64> for PropertyValue {
211 fn from(n: f64) -> Self {
212 PropertyValue::Number(n)
213 }
214}
215
216impl From<&f64> for PropertyValue {
217 fn from(n: &f64) -> Self {
218 PropertyValue::Number(*n)
219 }
220}
221
222impl From<i32> for PropertyValue {
223 fn from(n: i32) -> Self {
224 PropertyValue::Number(n as f64)
225 }
226}
227
228impl From<&i32> for PropertyValue {
229 fn from(n: &i32) -> Self {
230 PropertyValue::Number(*n as f64)
231 }
232}
233
234impl From<u32> for PropertyValue {
235 fn from(n: u32) -> Self {
236 PropertyValue::Number(n as f64)
237 }
238}
239
240impl From<&u32> for PropertyValue {
241 fn from(n: &u32) -> Self {
242 PropertyValue::Number(*n as f64)
243 }
244}
245
246pub type AdditionalProperties =
247 Box<dyn Iterator<Item = (Cow<'static, str>, PropertyValue)>>;
248
249pub trait JsErrorClass:
255 std::error::Error + Send + Sync + Any + 'static
256{
257 fn get_class(&self) -> Cow<'static, str>;
259
260 fn get_message(&self) -> Cow<'static, str>;
262
263 fn get_additional_properties(&self) -> AdditionalProperties;
265
266 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static);
267}
268
269#[macro_export]
288macro_rules! js_error_wrapper {
289 ($err_path:path, $err_name:ident, $js_err_type:tt) => {
290 deno_error::js_error_wrapper!($err_path, $err_name, |_error| $js_err_type);
291 };
292 ($err_path:path, $err_name:ident, |$inner:ident| $js_err_type:tt) => {
293 #[derive(Debug)]
294 pub struct $err_name(pub $err_path);
295 impl From<$err_path> for $err_name {
296 fn from(err: $err_path) -> Self {
297 Self(err)
298 }
299 }
300 impl $err_name {
301 pub fn get_error_class(
302 $inner: &$err_path,
303 ) -> impl Into<std::borrow::Cow<'static, str>> {
304 $js_err_type
305 }
306 }
307 impl std::error::Error for $err_name {
308 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
309 std::error::Error::source(&self.0)
310 }
311 }
312 impl std::fmt::Display for $err_name {
313 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
314 std::fmt::Display::fmt(&self.0, f)
315 }
316 }
317 impl deno_error::JsErrorClass for $err_name {
318 fn get_class(&self) -> std::borrow::Cow<'static, str> {
319 Self::get_error_class(&self.0).into()
320 }
321 fn get_message(&self) -> std::borrow::Cow<'static, str> {
322 self.to_string().into()
323 }
324 fn get_additional_properties(&self) -> deno_error::AdditionalProperties {
325 Box::new(std::iter::empty())
326 }
327 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
328 self
329 }
330 }
331 impl std::ops::Deref for $err_name {
332 type Target = $err_path;
333
334 fn deref(&self) -> &Self::Target {
335 &self.0
336 }
337 }
338 };
339}
340
341impl<T: JsErrorClass> JsErrorClass for Box<T> {
342 fn get_class(&self) -> Cow<'static, str> {
343 (**self).get_class()
344 }
345
346 fn get_message(&self) -> Cow<'static, str> {
347 (**self).get_message()
348 }
349
350 fn get_additional_properties(&self) -> AdditionalProperties {
351 (**self).get_additional_properties()
352 }
353
354 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
355 self
356 }
357}
358
359impl JsErrorClass for std::io::Error {
360 fn get_class(&self) -> Cow<'static, str> {
361 use std::io::ErrorKind::*;
362
363 let class = match self.kind() {
364 NotFound => "NotFound",
365 PermissionDenied => "PermissionDenied",
366 ConnectionRefused => "ConnectionRefused",
367 ConnectionReset => "ConnectionReset",
368 ConnectionAborted => "ConnectionAborted",
369 NotConnected => "NotConnected",
370 AddrInUse => "AddrInUse",
371 AddrNotAvailable => "AddrNotAvailable",
372 BrokenPipe => "BrokenPipe",
373 AlreadyExists => "AlreadyExists",
374 InvalidInput => TYPE_ERROR,
375 InvalidData => "InvalidData",
376 TimedOut => "TimedOut",
377 Interrupted => "Interrupted",
378 WriteZero => "WriteZero",
379 UnexpectedEof => "UnexpectedEof",
380 Other => GENERIC_ERROR,
381 WouldBlock => "WouldBlock",
382 IsADirectory => "IsADirectory",
383 NetworkUnreachable => "NetworkUnreachable",
384 NotADirectory => "NotADirectory",
385 kind => match format!("{kind:?}").as_str() {
386 "FilesystemLoop" => "FilesystemLoop",
387 _ => GENERIC_ERROR,
388 },
389 };
390
391 Cow::Borrowed(class)
392 }
393
394 fn get_message(&self) -> Cow<'static, str> {
395 self.to_string().into()
396 }
397
398 fn get_additional_properties(&self) -> AdditionalProperties {
399 if let Some(code) = get_error_code(self) {
400 Box::new(std::iter::once((
401 "code".into(),
402 PropertyValue::String(code.into()),
403 )))
404 } else {
405 Box::new(Box::new(std::iter::empty()))
406 }
407 }
408
409 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
410 self
411 }
412}
413
414impl JsErrorClass for std::env::VarError {
415 fn get_class(&self) -> Cow<'static, str> {
416 Cow::Borrowed(match self {
417 std::env::VarError::NotPresent => "NotFound",
418 std::env::VarError::NotUnicode(..) => "InvalidData",
419 })
420 }
421
422 fn get_message(&self) -> Cow<'static, str> {
423 self.to_string().into()
424 }
425
426 fn get_additional_properties(&self) -> AdditionalProperties {
427 Box::new(std::iter::empty())
428 }
429
430 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
431 self
432 }
433}
434
435impl JsErrorClass for std::sync::mpsc::RecvError {
436 fn get_class(&self) -> Cow<'static, str> {
437 Cow::Borrowed(GENERIC_ERROR)
438 }
439
440 fn get_message(&self) -> Cow<'static, str> {
441 self.to_string().into()
442 }
443
444 fn get_additional_properties(&self) -> AdditionalProperties {
445 Box::new(std::iter::empty())
446 }
447
448 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
449 self
450 }
451}
452
453impl JsErrorClass for std::str::Utf8Error {
454 fn get_class(&self) -> Cow<'static, str> {
455 Cow::Borrowed(GENERIC_ERROR)
456 }
457
458 fn get_message(&self) -> Cow<'static, str> {
459 self.to_string().into()
460 }
461
462 fn get_additional_properties(&self) -> AdditionalProperties {
463 Box::new(std::iter::empty())
464 }
465
466 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
467 self
468 }
469}
470
471impl JsErrorClass for std::num::TryFromIntError {
472 fn get_class(&self) -> Cow<'static, str> {
473 Cow::Borrowed(TYPE_ERROR)
474 }
475
476 fn get_message(&self) -> Cow<'static, str> {
477 self.to_string().into()
478 }
479
480 fn get_additional_properties(&self) -> AdditionalProperties {
481 Box::new(std::iter::empty())
482 }
483
484 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
485 self
486 }
487}
488
489impl JsErrorClass for std::convert::Infallible {
490 fn get_class(&self) -> Cow<'static, str> {
491 unreachable!()
492 }
493
494 fn get_message(&self) -> Cow<'static, str> {
495 unreachable!()
496 }
497
498 fn get_additional_properties(&self) -> AdditionalProperties {
499 unreachable!();
500 }
501
502 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
503 unreachable!()
504 }
505}
506
507#[cfg(all(feature = "serde", feature = "serde_json"))]
508impl JsErrorClass for serde_json::Error {
509 fn get_class(&self) -> Cow<'static, str> {
510 use serde::de::StdError;
511 use serde_json::error::*;
512
513 match self.classify() {
514 Category::Io => self
515 .source()
516 .and_then(|e| e.downcast_ref::<std::io::Error>())
517 .unwrap()
518 .get_class(),
519 Category::Syntax => Cow::Borrowed(SYNTAX_ERROR),
520 Category::Data => Cow::Borrowed("InvalidData"),
521 Category::Eof => Cow::Borrowed("UnexpectedEof"),
522 }
523 }
524
525 fn get_message(&self) -> Cow<'static, str> {
526 self.to_string().into()
527 }
528
529 fn get_additional_properties(&self) -> AdditionalProperties {
530 Box::new(std::iter::empty()) }
532
533 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
534 self
535 }
536}
537
538#[cfg(feature = "url")]
539impl JsErrorClass for url::ParseError {
540 fn get_class(&self) -> Cow<'static, str> {
541 Cow::Borrowed(URI_ERROR)
542 }
543
544 fn get_message(&self) -> Cow<'static, str> {
545 self.to_string().into()
546 }
547
548 fn get_additional_properties(&self) -> AdditionalProperties {
549 Box::new(std::iter::empty())
550 }
551
552 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
553 self
554 }
555}
556
557#[cfg(feature = "tokio")]
558impl<T: Send + Sync + 'static> JsErrorClass
559 for tokio::sync::mpsc::error::SendError<T>
560{
561 fn get_class(&self) -> Cow<'static, str> {
562 Cow::Borrowed(GENERIC_ERROR)
563 }
564
565 fn get_message(&self) -> Cow<'static, str> {
566 self.to_string().into()
567 }
568
569 fn get_additional_properties(&self) -> AdditionalProperties {
570 Box::new(std::iter::empty())
571 }
572
573 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
574 self
575 }
576}
577
578#[cfg(feature = "tokio")]
579impl JsErrorClass for tokio::task::JoinError {
580 fn get_class(&self) -> Cow<'static, str> {
581 Cow::Borrowed(GENERIC_ERROR)
582 }
583
584 fn get_message(&self) -> Cow<'static, str> {
585 self.to_string().into()
586 }
587
588 fn get_additional_properties(&self) -> AdditionalProperties {
589 Box::new(std::iter::empty())
590 }
591
592 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
593 self
594 }
595}
596
597#[cfg(feature = "tokio")]
598impl JsErrorClass for tokio::sync::broadcast::error::RecvError {
599 fn get_class(&self) -> Cow<'static, str> {
600 Cow::Borrowed(GENERIC_ERROR)
601 }
602
603 fn get_message(&self) -> Cow<'static, str> {
604 self.to_string().into()
605 }
606
607 fn get_additional_properties(&self) -> AdditionalProperties {
608 Box::new(std::iter::empty())
609 }
610
611 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
612 self
613 }
614}
615
616enum JsErrorBoxInner {
617 Standalone {
618 class: Cow<'static, str>,
619 message: Cow<'static, str>,
620 },
621 Wrap(Box<dyn JsErrorClass>),
622}
623
624pub struct JsErrorBox(JsErrorBoxInner);
625
626impl std::fmt::Debug for JsErrorBox {
627 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
628 let mut debug = f.debug_struct("JsErrorBox");
629
630 match &self.0 {
631 JsErrorBoxInner::Standalone { class, message } => {
632 debug.field("class", class);
633 debug.field("message", message);
634 }
635 JsErrorBoxInner::Wrap(inner) => {
636 debug.field("inner", inner);
637 }
638 }
639
640 debug.finish()
641 }
642}
643
644impl std::fmt::Display for JsErrorBox {
645 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
646 write!(f, "{}", self.get_message())
647 }
648}
649
650impl std::error::Error for JsErrorBox {
651 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
652 match &self.0 {
653 JsErrorBoxInner::Standalone { .. } => None,
654 JsErrorBoxInner::Wrap(inner) => inner.source(),
655 }
656 }
657}
658
659impl JsErrorClass for JsErrorBox {
660 fn get_class(&self) -> Cow<'static, str> {
661 match &self.0 {
662 JsErrorBoxInner::Standalone { class, .. } => class.clone(),
663 JsErrorBoxInner::Wrap(inner) => inner.get_class(),
664 }
665 }
666
667 fn get_message(&self) -> Cow<'static, str> {
668 match &self.0 {
669 JsErrorBoxInner::Standalone { message, .. } => message.clone(),
670 JsErrorBoxInner::Wrap(inner) => inner.get_message(),
671 }
672 }
673
674 fn get_additional_properties(&self) -> AdditionalProperties {
675 match &self.0 {
676 JsErrorBoxInner::Standalone { .. } => Box::new(std::iter::empty()),
677 JsErrorBoxInner::Wrap(inner) => inner.get_additional_properties(),
678 }
679 }
680
681 fn get_ref(&self) -> &(dyn std::error::Error + Send + Sync + 'static) {
682 match &self.0 {
683 JsErrorBoxInner::Standalone { .. } => self,
684 JsErrorBoxInner::Wrap(inner) => inner.get_ref(),
685 }
686 }
687}
688
689impl JsErrorBox {
690 pub fn new(
691 class: impl Into<Cow<'static, str>>,
692 message: impl Into<Cow<'static, str>>,
693 ) -> Self {
694 Self(JsErrorBoxInner::Standalone {
695 class: class.into(),
696 message: message.into(),
697 })
698 }
699
700 pub fn from_err<T: JsErrorClass>(err: T) -> Self {
701 Self(JsErrorBoxInner::Wrap(Box::new(err)))
702 }
703
704 pub fn generic(message: impl Into<Cow<'static, str>>) -> JsErrorBox {
705 Self::new(GENERIC_ERROR, message)
706 }
707
708 pub fn type_error(message: impl Into<Cow<'static, str>>) -> JsErrorBox {
709 Self::new(TYPE_ERROR, message)
710 }
711
712 pub fn range_error(message: impl Into<Cow<'static, str>>) -> JsErrorBox {
713 Self::new(RANGE_ERROR, message)
714 }
715
716 pub fn uri_error(message: impl Into<Cow<'static, str>>) -> JsErrorBox {
717 Self::new(URI_ERROR, message)
718 }
719
720 pub fn not_supported() -> JsErrorBox {
722 Self::new(NOT_SUPPORTED_ERROR, "The operation is not supported")
723 }
724
725 pub fn get_inner_ref(
726 &self,
727 ) -> Option<&(dyn std::error::Error + Send + Sync + 'static)> {
728 match &self.0 {
729 JsErrorBoxInner::Standalone { .. } => None,
730 JsErrorBoxInner::Wrap(inner) => Some(inner.get_ref()),
731 }
732 }
733}
734
735#[cfg(test)]
736mod tests {
737 use std::io;
738
739 use super::JsErrorClass;
740
741 #[test]
742 fn test_io_error_class_stable() {
743 assert_eq!(
744 io::Error::new(io::ErrorKind::NotFound, "").get_class(),
745 "NotFound",
746 );
747 }
748
749 #[test]
750 #[cfg(unix)]
751 fn test_io_error_class_unstable() {
752 assert_eq!(
753 io::Error::from_raw_os_error(libc::ELOOP).get_class(),
754 "FilesystemLoop",
755 );
756 }
757}