summaryrefslogtreecommitdiff
path: root/cli/deno_error.rs
diff options
context:
space:
mode:
Diffstat (limited to 'cli/deno_error.rs')
-rw-r--r--cli/deno_error.rs513
1 files changed, 513 insertions, 0 deletions
diff --git a/cli/deno_error.rs b/cli/deno_error.rs
new file mode 100644
index 000000000..551547e26
--- /dev/null
+++ b/cli/deno_error.rs
@@ -0,0 +1,513 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+use crate::diagnostics::Diagnostic;
+use crate::fmt_errors::JSError;
+use crate::import_map::ImportMapError;
+pub use crate::msg::ErrorKind;
+use deno::AnyError;
+use deno::ErrBox;
+use deno::ModuleResolutionError;
+use http::uri;
+use hyper;
+use reqwest;
+use rustyline::error::ReadlineError;
+use std;
+use std::env::VarError;
+use std::error::Error;
+use std::fmt;
+use std::io;
+use url;
+
+#[derive(Debug)]
+pub struct DenoError {
+ kind: ErrorKind,
+ msg: String,
+}
+
+pub fn print_err_and_exit(err: ErrBox) {
+ eprintln!("{}", err.to_string());
+ std::process::exit(1);
+}
+
+pub fn js_check(r: Result<(), ErrBox>) {
+ if let Err(err) = r {
+ print_err_and_exit(err);
+ }
+}
+
+impl DenoError {
+ pub fn new(kind: ErrorKind, msg: String) -> Self {
+ Self { kind, msg }
+ }
+}
+
+impl Error for DenoError {}
+
+impl fmt::Display for DenoError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad(self.msg.as_str())
+ }
+}
+
+#[derive(Debug)]
+struct StaticError(ErrorKind, &'static str);
+
+impl Error for StaticError {}
+
+impl fmt::Display for StaticError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.pad(self.1)
+ }
+}
+
+pub fn bad_resource() -> ErrBox {
+ StaticError(ErrorKind::BadResource, "bad resource id").into()
+}
+
+pub fn permission_denied() -> ErrBox {
+ StaticError(ErrorKind::PermissionDenied, "permission denied").into()
+}
+
+pub fn op_not_implemented() -> ErrBox {
+ StaticError(ErrorKind::OpNotAvailable, "op not implemented").into()
+}
+
+pub fn no_buffer_specified() -> ErrBox {
+ StaticError(ErrorKind::InvalidInput, "no buffer specified").into()
+}
+
+pub fn no_async_support() -> ErrBox {
+ StaticError(ErrorKind::NoAsyncSupport, "op doesn't support async calls")
+ .into()
+}
+
+pub fn no_sync_support() -> ErrBox {
+ StaticError(ErrorKind::NoSyncSupport, "op doesn't support sync calls").into()
+}
+
+pub fn invalid_address_syntax() -> ErrBox {
+ StaticError(ErrorKind::InvalidInput, "invalid address syntax").into()
+}
+
+pub fn too_many_redirects() -> ErrBox {
+ StaticError(ErrorKind::TooManyRedirects, "too many redirects").into()
+}
+
+pub trait GetErrorKind {
+ fn kind(&self) -> ErrorKind;
+}
+
+impl GetErrorKind for DenoError {
+ fn kind(&self) -> ErrorKind {
+ self.kind
+ }
+}
+
+impl GetErrorKind for StaticError {
+ fn kind(&self) -> ErrorKind {
+ self.0
+ }
+}
+
+impl GetErrorKind for JSError {
+ fn kind(&self) -> ErrorKind {
+ ErrorKind::JSError
+ }
+}
+
+impl GetErrorKind for Diagnostic {
+ fn kind(&self) -> ErrorKind {
+ ErrorKind::Diagnostic
+ }
+}
+
+impl GetErrorKind for ImportMapError {
+ fn kind(&self) -> ErrorKind {
+ ErrorKind::ImportMapError
+ }
+}
+
+impl GetErrorKind for ModuleResolutionError {
+ fn kind(&self) -> ErrorKind {
+ use ModuleResolutionError::*;
+ match self {
+ InvalidUrl(ref err) | InvalidBaseUrl(ref err) => err.kind(),
+ InvalidPath(_) => ErrorKind::InvalidPath,
+ ImportPrefixMissing(_) => ErrorKind::ImportPrefixMissing,
+ }
+ }
+}
+
+impl GetErrorKind for VarError {
+ fn kind(&self) -> ErrorKind {
+ use VarError::*;
+ match self {
+ NotPresent => ErrorKind::NotFound,
+ NotUnicode(..) => ErrorKind::InvalidData,
+ }
+ }
+}
+
+impl GetErrorKind for io::Error {
+ fn kind(&self) -> ErrorKind {
+ use io::ErrorKind::*;
+ match self.kind() {
+ NotFound => ErrorKind::NotFound,
+ PermissionDenied => ErrorKind::PermissionDenied,
+ ConnectionRefused => ErrorKind::ConnectionRefused,
+ ConnectionReset => ErrorKind::ConnectionReset,
+ ConnectionAborted => ErrorKind::ConnectionAborted,
+ NotConnected => ErrorKind::NotConnected,
+ AddrInUse => ErrorKind::AddrInUse,
+ AddrNotAvailable => ErrorKind::AddrNotAvailable,
+ BrokenPipe => ErrorKind::BrokenPipe,
+ AlreadyExists => ErrorKind::AlreadyExists,
+ WouldBlock => ErrorKind::WouldBlock,
+ InvalidInput => ErrorKind::InvalidInput,
+ InvalidData => ErrorKind::InvalidData,
+ TimedOut => ErrorKind::TimedOut,
+ Interrupted => ErrorKind::Interrupted,
+ WriteZero => ErrorKind::WriteZero,
+ UnexpectedEof => ErrorKind::UnexpectedEof,
+ _ => ErrorKind::Other,
+ }
+ }
+}
+
+impl GetErrorKind for uri::InvalidUri {
+ fn kind(&self) -> ErrorKind {
+ // The http::uri::ErrorKind exists and is similar to url::ParseError.
+ // However it is also private, so we can't get any details out.
+ ErrorKind::InvalidUri
+ }
+}
+
+impl GetErrorKind for url::ParseError {
+ fn kind(&self) -> ErrorKind {
+ use url::ParseError::*;
+ match self {
+ EmptyHost => ErrorKind::EmptyHost,
+ IdnaError => ErrorKind::IdnaError,
+ InvalidDomainCharacter => ErrorKind::InvalidDomainCharacter,
+ InvalidIpv4Address => ErrorKind::InvalidIpv4Address,
+ InvalidIpv6Address => ErrorKind::InvalidIpv6Address,
+ InvalidPort => ErrorKind::InvalidPort,
+ Overflow => ErrorKind::Overflow,
+ RelativeUrlWithCannotBeABaseBase => {
+ ErrorKind::RelativeUrlWithCannotBeABaseBase
+ }
+ RelativeUrlWithoutBase => ErrorKind::RelativeUrlWithoutBase,
+ SetHostOnCannotBeABaseUrl => ErrorKind::SetHostOnCannotBeABaseUrl,
+ }
+ }
+}
+
+impl GetErrorKind for hyper::Error {
+ fn kind(&self) -> ErrorKind {
+ match self {
+ e if e.is_canceled() => ErrorKind::HttpCanceled,
+ e if e.is_closed() => ErrorKind::HttpClosed,
+ e if e.is_parse() => ErrorKind::HttpParse,
+ e if e.is_user() => ErrorKind::HttpUser,
+ _ => ErrorKind::HttpOther,
+ }
+ }
+}
+
+impl GetErrorKind for reqwest::Error {
+ fn kind(&self) -> ErrorKind {
+ use self::GetErrorKind as Get;
+
+ match self.get_ref() {
+ Some(err_ref) => None
+ .or_else(|| err_ref.downcast_ref::<hyper::Error>().map(Get::kind))
+ .or_else(|| err_ref.downcast_ref::<url::ParseError>().map(Get::kind))
+ .or_else(|| err_ref.downcast_ref::<io::Error>().map(Get::kind))
+ .or_else(|| {
+ err_ref
+ .downcast_ref::<serde_json::error::Error>()
+ .map(Get::kind)
+ })
+ .unwrap_or_else(|| ErrorKind::HttpOther),
+ _ => ErrorKind::HttpOther,
+ }
+ }
+}
+
+impl GetErrorKind for ReadlineError {
+ fn kind(&self) -> ErrorKind {
+ use ReadlineError::*;
+ match self {
+ Io(err) => GetErrorKind::kind(err),
+ Eof => ErrorKind::UnexpectedEof,
+ Interrupted => ErrorKind::Interrupted,
+ #[cfg(unix)]
+ Errno(err) => err.kind(),
+ _ => unimplemented!(),
+ }
+ }
+}
+
+impl GetErrorKind for serde_json::error::Error {
+ fn kind(&self) -> ErrorKind {
+ use serde_json::error::*;
+ match self.classify() {
+ Category::Io => ErrorKind::InvalidInput,
+ Category::Syntax => ErrorKind::InvalidInput,
+ Category::Data => ErrorKind::InvalidData,
+ Category::Eof => ErrorKind::UnexpectedEof,
+ }
+ }
+}
+
+#[cfg(unix)]
+mod unix {
+ use super::{ErrorKind, GetErrorKind};
+ use nix::errno::Errno::*;
+ pub use nix::Error;
+ use nix::Error::Sys;
+
+ impl GetErrorKind for Error {
+ fn kind(&self) -> ErrorKind {
+ match self {
+ Sys(EPERM) => ErrorKind::PermissionDenied,
+ Sys(EINVAL) => ErrorKind::InvalidInput,
+ Sys(ENOENT) => ErrorKind::NotFound,
+ Sys(_) => ErrorKind::UnixError,
+ _ => ErrorKind::Other,
+ }
+ }
+ }
+}
+
+impl GetErrorKind for dyn AnyError {
+ fn kind(&self) -> ErrorKind {
+ use self::GetErrorKind as Get;
+
+ #[cfg(unix)]
+ fn unix_error_kind(err: &dyn AnyError) -> Option<ErrorKind> {
+ err.downcast_ref::<unix::Error>().map(Get::kind)
+ }
+
+ #[cfg(not(unix))]
+ fn unix_error_kind(_: &dyn AnyError) -> Option<ErrorKind> {
+ None
+ }
+
+ None
+ .or_else(|| self.downcast_ref::<DenoError>().map(Get::kind))
+ .or_else(|| self.downcast_ref::<Diagnostic>().map(Get::kind))
+ .or_else(|| self.downcast_ref::<hyper::Error>().map(Get::kind))
+ .or_else(|| self.downcast_ref::<reqwest::Error>().map(Get::kind))
+ .or_else(|| self.downcast_ref::<ImportMapError>().map(Get::kind))
+ .or_else(|| self.downcast_ref::<io::Error>().map(Get::kind))
+ .or_else(|| self.downcast_ref::<JSError>().map(Get::kind))
+ .or_else(|| self.downcast_ref::<ModuleResolutionError>().map(Get::kind))
+ .or_else(|| self.downcast_ref::<StaticError>().map(Get::kind))
+ .or_else(|| self.downcast_ref::<uri::InvalidUri>().map(Get::kind))
+ .or_else(|| self.downcast_ref::<url::ParseError>().map(Get::kind))
+ .or_else(|| self.downcast_ref::<VarError>().map(Get::kind))
+ .or_else(|| self.downcast_ref::<ReadlineError>().map(Get::kind))
+ .or_else(|| {
+ self
+ .downcast_ref::<serde_json::error::Error>()
+ .map(Get::kind)
+ })
+ .or_else(|| unix_error_kind(self))
+ .unwrap_or_else(|| {
+ panic!("Can't get ErrorKind for {:?}", self);
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::colors::strip_ansi_codes;
+ use crate::diagnostics::Diagnostic;
+ use crate::diagnostics::DiagnosticCategory;
+ use crate::diagnostics::DiagnosticItem;
+ use deno::ErrBox;
+ use deno::StackFrame;
+ use deno::V8Exception;
+
+ fn js_error() -> JSError {
+ JSError::new(V8Exception {
+ message: "Error: foo bar".to_string(),
+ source_line: None,
+ script_resource_name: None,
+ line_number: None,
+ start_position: None,
+ end_position: None,
+ error_level: None,
+ start_column: None,
+ end_column: None,
+ frames: vec![
+ StackFrame {
+ line: 4,
+ column: 16,
+ script_name: "foo_bar.ts".to_string(),
+ function_name: "foo".to_string(),
+ is_eval: false,
+ is_constructor: false,
+ is_wasm: false,
+ },
+ StackFrame {
+ line: 5,
+ column: 20,
+ script_name: "bar_baz.ts".to_string(),
+ function_name: "qat".to_string(),
+ is_eval: false,
+ is_constructor: false,
+ is_wasm: false,
+ },
+ StackFrame {
+ line: 1,
+ column: 1,
+ script_name: "deno_main.js".to_string(),
+ function_name: "".to_string(),
+ is_eval: false,
+ is_constructor: false,
+ is_wasm: false,
+ },
+ ],
+ })
+ }
+
+ fn diagnostic() -> Diagnostic {
+ Diagnostic {
+ items: vec![
+ DiagnosticItem {
+ message: "Example 1".to_string(),
+ message_chain: None,
+ code: 2322,
+ category: DiagnosticCategory::Error,
+ start_position: Some(267),
+ end_position: Some(273),
+ source_line: Some(" values: o => [".to_string()),
+ line_number: Some(18),
+ script_resource_name: Some(
+ "deno/tests/complex_diagnostics.ts".to_string(),
+ ),
+ start_column: Some(2),
+ end_column: Some(8),
+ related_information: None,
+ },
+ DiagnosticItem {
+ message: "Example 2".to_string(),
+ message_chain: None,
+ code: 2000,
+ category: DiagnosticCategory::Error,
+ start_position: Some(2),
+ end_position: Some(2),
+ source_line: Some(" values: undefined,".to_string()),
+ line_number: Some(128),
+ script_resource_name: Some("/foo/bar.ts".to_string()),
+ start_column: Some(2),
+ end_column: Some(8),
+ related_information: None,
+ },
+ ],
+ }
+ }
+
+ fn io_error() -> io::Error {
+ io::Error::from(io::ErrorKind::NotFound)
+ }
+
+ fn url_error() -> url::ParseError {
+ url::ParseError::EmptyHost
+ }
+
+ fn import_map_error() -> ImportMapError {
+ ImportMapError {
+ msg: "an import map error".to_string(),
+ }
+ }
+
+ #[test]
+ fn test_simple_error() {
+ let err =
+ ErrBox::from(DenoError::new(ErrorKind::NoError, "foo".to_string()));
+ assert_eq!(err.kind(), ErrorKind::NoError);
+ assert_eq!(err.to_string(), "foo");
+ }
+
+ #[test]
+ fn test_io_error() {
+ let err = ErrBox::from(io_error());
+ assert_eq!(err.kind(), ErrorKind::NotFound);
+ assert_eq!(err.to_string(), "entity not found");
+ }
+
+ #[test]
+ fn test_url_error() {
+ let err = ErrBox::from(url_error());
+ assert_eq!(err.kind(), ErrorKind::EmptyHost);
+ assert_eq!(err.to_string(), "empty host");
+ }
+
+ // TODO find a way to easily test tokio errors and unix errors
+
+ #[test]
+ fn test_diagnostic() {
+ let err = ErrBox::from(diagnostic());
+ assert_eq!(err.kind(), ErrorKind::Diagnostic);
+ assert_eq!(strip_ansi_codes(&err.to_string()), "error TS2322: Example 1\n\n► deno/tests/complex_diagnostics.ts:19:3\n\n19 values: o => [\n ~~~~~~\n\nerror TS2000: Example 2\n\n► /foo/bar.ts:129:3\n\n129 values: undefined,\n ~~~~~~\n\n\nFound 2 errors.\n");
+ }
+
+ #[test]
+ fn test_js_error() {
+ let err = ErrBox::from(js_error());
+ assert_eq!(err.kind(), ErrorKind::JSError);
+ assert_eq!(strip_ansi_codes(&err.to_string()), "error: Error: foo bar\n at foo (foo_bar.ts:5:17)\n at qat (bar_baz.ts:6:21)\n at deno_main.js:2:2");
+ }
+
+ #[test]
+ fn test_import_map_error() {
+ let err = ErrBox::from(import_map_error());
+ assert_eq!(err.kind(), ErrorKind::ImportMapError);
+ assert_eq!(err.to_string(), "an import map error");
+ }
+
+ #[test]
+ fn test_bad_resource() {
+ let err = bad_resource();
+ assert_eq!(err.kind(), ErrorKind::BadResource);
+ assert_eq!(err.to_string(), "bad resource id");
+ }
+
+ #[test]
+ fn test_permission_denied() {
+ let err = permission_denied();
+ assert_eq!(err.kind(), ErrorKind::PermissionDenied);
+ assert_eq!(err.to_string(), "permission denied");
+ }
+
+ #[test]
+ fn test_op_not_implemented() {
+ let err = op_not_implemented();
+ assert_eq!(err.kind(), ErrorKind::OpNotAvailable);
+ assert_eq!(err.to_string(), "op not implemented");
+ }
+
+ #[test]
+ fn test_no_buffer_specified() {
+ let err = no_buffer_specified();
+ assert_eq!(err.kind(), ErrorKind::InvalidInput);
+ assert_eq!(err.to_string(), "no buffer specified");
+ }
+
+ #[test]
+ fn test_no_async_support() {
+ let err = no_async_support();
+ assert_eq!(err.kind(), ErrorKind::NoAsyncSupport);
+ assert_eq!(err.to_string(), "op doesn't support async calls");
+ }
+
+ #[test]
+ fn test_no_sync_support() {
+ let err = no_sync_support();
+ assert_eq!(err.kind(), ErrorKind::NoSyncSupport);
+ assert_eq!(err.to_string(), "op doesn't support sync calls");
+ }
+}