#![deny(clippy::unnecessary_wraps)]
#![deny(clippy::print_stderr)]
#![deny(clippy::print_stdout)]
mod error_codes;
pub use deno_error_macro::*;
pub use error_codes::*;
use std::any::Any;
use std::borrow::Cow;
pub mod builtin_classes {
pub const GENERIC_ERROR: &str = "Error";
pub const RANGE_ERROR: &str = "RangeError";
pub const TYPE_ERROR: &str = "TypeError";
pub const SYNTAX_ERROR: &str = "SyntaxError";
pub const URI_ERROR: &str = "URIError";
pub const REFERENCE_ERROR: &str = "ReferenceError";
pub const NOT_SUPPORTED_ERROR: &str = "NotSupported";
}
use builtin_classes::*;
pub trait JsErrorClass:
std::error::Error + Send + Sync + Any + 'static
{
fn get_class(&self) -> Cow<'static, str>;
fn get_message(&self) -> Cow<'static, str>;
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)>;
fn as_any(&self) -> &dyn Any;
}
#[macro_export]
macro_rules! js_error_wrapper {
($err_path:path, $err_name:ident, $js_err_type:tt) => {
deno_error::js_error_wrapper!($err_path, $err_name, |_error| $js_err_type);
};
($err_path:path, $err_name:ident, |$inner:ident| $js_err_type:tt) => {
#[derive(Debug)]
pub struct $err_name(pub $err_path);
impl From<$err_path> for $err_name {
fn from(err: $err_path) -> Self {
Self(err)
}
}
impl $err_name {
pub fn get_error_class(
$inner: &$err_path,
) -> impl Into<std::borrow::Cow<'static, str>> {
$js_err_type
}
}
impl std::error::Error for $err_name {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
std::error::Error::source(&self.0)
}
}
impl std::fmt::Display for $err_name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
std::fmt::Display::fmt(&self.0, f)
}
}
impl deno_error::JsErrorClass for $err_name {
fn get_class(&self) -> std::borrow::Cow<'static, str> {
Self::get_error_class(&self.0).into()
}
fn get_message(&self) -> std::borrow::Cow<'static, str> {
self.to_string().into()
}
fn get_additional_properties(
&self,
) -> Vec<(
std::borrow::Cow<'static, str>,
std::borrow::Cow<'static, str>,
)> {
vec![]
}
fn as_any(&self) -> &dyn std::any::Any {
self
}
}
impl std::ops::Deref for $err_name {
type Target = $err_path;
fn deref(&self) -> &Self::Target {
&self.0
}
}
};
}
impl<T: JsErrorClass> JsErrorClass for Box<T> {
fn get_class(&self) -> Cow<'static, str> {
(**self).get_class()
}
fn get_message(&self) -> Cow<'static, str> {
(**self).get_message()
}
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)> {
(**self).get_additional_properties()
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl JsErrorClass for std::io::Error {
fn get_class(&self) -> Cow<'static, str> {
use std::io::ErrorKind::*;
let class = match self.kind() {
NotFound => "NotFound",
PermissionDenied => "PermissionDenied",
ConnectionRefused => "ConnectionRefused",
ConnectionReset => "ConnectionReset",
ConnectionAborted => "ConnectionAborted",
NotConnected => "NotConnected",
AddrInUse => "AddrInUse",
AddrNotAvailable => "AddrNotAvailable",
BrokenPipe => "BrokenPipe",
AlreadyExists => "AlreadyExists",
InvalidInput => TYPE_ERROR,
InvalidData => "InvalidData",
TimedOut => "TimedOut",
Interrupted => "Interrupted",
WriteZero => "WriteZero",
UnexpectedEof => "UnexpectedEof",
Other => GENERIC_ERROR,
WouldBlock => "WouldBlock",
kind => {
let kind_str = kind.to_string();
match kind_str.as_str() {
"FilesystemLoop" => "FilesystemLoop",
"IsADirectory" => "IsADirectory",
"NetworkUnreachable" => "NetworkUnreachable",
"NotADirectory" => "NotADirectory",
_ => GENERIC_ERROR,
}
}
};
Cow::Borrowed(class)
}
fn get_message(&self) -> Cow<'static, str> {
self.to_string().into()
}
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)> {
get_error_code(self)
.map(|code| vec![("code".into(), code.into())])
.unwrap_or_default()
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl JsErrorClass for std::env::VarError {
fn get_class(&self) -> Cow<'static, str> {
Cow::Borrowed(match self {
std::env::VarError::NotPresent => "NotFound",
std::env::VarError::NotUnicode(..) => "InvalidData",
})
}
fn get_message(&self) -> Cow<'static, str> {
self.to_string().into()
}
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)> {
vec![]
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl JsErrorClass for std::sync::mpsc::RecvError {
fn get_class(&self) -> Cow<'static, str> {
Cow::Borrowed(GENERIC_ERROR)
}
fn get_message(&self) -> Cow<'static, str> {
self.to_string().into()
}
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)> {
vec![]
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl JsErrorClass for std::str::Utf8Error {
fn get_class(&self) -> Cow<'static, str> {
Cow::Borrowed(GENERIC_ERROR)
}
fn get_message(&self) -> Cow<'static, str> {
self.to_string().into()
}
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)> {
vec![]
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl JsErrorClass for std::num::TryFromIntError {
fn get_class(&self) -> Cow<'static, str> {
Cow::Borrowed(TYPE_ERROR)
}
fn get_message(&self) -> Cow<'static, str> {
self.to_string().into()
}
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)> {
vec![]
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[cfg(all(feature = "serde", feature = "serde_json"))]
impl JsErrorClass for serde_json::Error {
fn get_class(&self) -> Cow<'static, str> {
use serde::de::StdError;
use serde_json::error::*;
match self.classify() {
Category::Io => self
.source()
.and_then(|e| e.downcast_ref::<std::io::Error>())
.unwrap()
.get_class(),
Category::Syntax => Cow::Borrowed(SYNTAX_ERROR),
Category::Data => Cow::Borrowed("InvalidData"),
Category::Eof => Cow::Borrowed("UnexpectedEof"),
}
}
fn get_message(&self) -> Cow<'static, str> {
self.to_string().into()
}
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)> {
vec![] }
fn as_any(&self) -> &dyn Any {
self
}
}
#[cfg(feature = "url")]
impl JsErrorClass for url::ParseError {
fn get_class(&self) -> Cow<'static, str> {
Cow::Borrowed(URI_ERROR)
}
fn get_message(&self) -> Cow<'static, str> {
self.to_string().into()
}
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)> {
vec![]
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[cfg(feature = "tokio")]
impl<T: Send + Sync + 'static> JsErrorClass
for tokio::sync::mpsc::error::SendError<T>
{
fn get_class(&self) -> Cow<'static, str> {
Cow::Borrowed(GENERIC_ERROR)
}
fn get_message(&self) -> Cow<'static, str> {
self.to_string().into()
}
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)> {
vec![]
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[cfg(feature = "tokio")]
impl JsErrorClass for tokio::task::JoinError {
fn get_class(&self) -> Cow<'static, str> {
Cow::Borrowed(GENERIC_ERROR)
}
fn get_message(&self) -> Cow<'static, str> {
self.to_string().into()
}
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)> {
vec![]
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[cfg(feature = "tokio")]
impl JsErrorClass for tokio::sync::broadcast::error::RecvError {
fn get_class(&self) -> Cow<'static, str> {
Cow::Borrowed(GENERIC_ERROR)
}
fn get_message(&self) -> Cow<'static, str> {
self.to_string().into()
}
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)> {
vec![]
}
fn as_any(&self) -> &dyn Any {
self
}
}
#[derive(Debug)]
pub struct JsErrorBox {
class: Cow<'static, str>,
message: Cow<'static, str>,
pub inner: Option<Box<dyn JsErrorClass>>,
}
impl std::fmt::Display for JsErrorBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for JsErrorBox {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
self.inner.as_ref().and_then(|e| e.source())
}
}
impl JsErrorClass for JsErrorBox {
fn get_class(&self) -> Cow<'static, str> {
self.class.clone()
}
fn get_message(&self) -> Cow<'static, str> {
self.message.clone()
}
fn get_additional_properties(
&self,
) -> Vec<(Cow<'static, str>, Cow<'static, str>)> {
self
.inner
.as_ref()
.map(|source| source.get_additional_properties())
.unwrap_or_default()
}
fn as_any(&self) -> &dyn Any {
if let Some(err) = &self.inner {
err.as_any()
} else {
self
}
}
}
impl JsErrorBox {
pub fn new(
class: impl Into<Cow<'static, str>>,
message: impl Into<Cow<'static, str>>,
) -> JsErrorBox {
JsErrorBox {
class: class.into(),
message: message.into(),
inner: None,
}
}
pub fn from_err<T: JsErrorClass>(err: T) -> Self {
Self {
class: err.get_class(),
message: err.get_message(),
inner: Some(Box::new(err)),
}
}
pub fn generic(message: impl Into<Cow<'static, str>>) -> JsErrorBox {
Self::new(GENERIC_ERROR, message)
}
pub fn type_error(message: impl Into<Cow<'static, str>>) -> JsErrorBox {
Self::new(TYPE_ERROR, message)
}
pub fn range_error(message: impl Into<Cow<'static, str>>) -> JsErrorBox {
Self::new(RANGE_ERROR, message)
}
pub fn uri_error(message: impl Into<Cow<'static, str>>) -> JsErrorBox {
Self::new(URI_ERROR, message)
}
pub fn not_supported() -> JsErrorBox {
Self::new(NOT_SUPPORTED_ERROR, "The operation is not supported")
}
}