diff --git a/Cargo.lock b/Cargo.lock index 3365f2ce..5dddf36a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5,7 +5,6 @@ name = "abscissa" version = "0.4.0" dependencies = [ "abscissa_core 0.4.0", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "gumdrop 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "handlebars 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -18,10 +17,10 @@ name = "abscissa_core" version = "0.4.0" dependencies = [ "abscissa_derive 0.4.0", + "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", "canonical-path 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "color-backtrace 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", "generational-arena 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "gumdrop 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -226,26 +225,6 @@ dependencies = [ "generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "failure" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "backtrace 0.3.38 (registry+https://github.com/rust-lang/crates.io-index)", - "failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "failure_derive" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "proc-macro2 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)", - "synstructure 0.12.1 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "fake-simd" version = "0.1.2" @@ -851,8 +830,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum darling_core 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "ee54512bec54b41cf2337a22ddfadb53c7d4c738494dc2a186d7b037ad683b85" "checksum darling_macro 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0cd3e432e52c0810b72898296a69d66b1d78d1517dff6cde7a130557a55a62c1" "checksum digest 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -"checksum failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f8273f13c977665c5db7eb2b99ae520952fe5ac831ae4cd09d80c4c7042b5ed9" -"checksum failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0bc225b78e0391e4b8683440bf2e63c2deeeb2ce5189eab46e2b68c6d3725d08" "checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" "checksum fallible-iterator 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" "checksum findshlibs 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b1260d61e4fe2a6ab845ffdc426a0bd68ffb240b91cf0ec5a8d1170cec535bd8" diff --git a/README.md b/README.md index 84c7b33e..1949ec02 100644 --- a/README.md +++ b/README.md @@ -27,8 +27,7 @@ or network/web services), aiming to provide a large number of features with a by default: `terminal` and `logging`. - **configuration**: Simple parsing of TOML configurations to `serde`-parsed configuration types which can be dynamically updated at runtime. -- **error handling**: generic `Error` type based on the `failure` crate, and a - unified error-handling subsystem. +- **error handling**: unified error-handling subsystem with generic error type. - **logging**: based on the `log` to provide application-level logging. - **secrets management**: the (optional) `secrets` module includes a `Secret` type which derives serde's `Deserialize` and can be used to represent secret @@ -103,29 +102,28 @@ default set of features in the application: | 7 | [canonical-path] | [iqlusion] | Apache-2.0 | Get canonical fs paths | | 8 | [chrono] | [chronotope] | Apache-2.0/MIT | Time/date library | | 9 | [color-backtrace] | [@athre0z] | Apache-2.0/MIT | Rich colored backtraces | -| 10 | [failure] | [@withoutboats] | Apache-2.0/MIT | Error handling | -| 11 | [generational-arena] | [@fitzgen] | MPL-2.0 | Component allocator | -| 12 | [gumdrop] | [@Murarth] | Apache-2.0/MIT | Command-line options | -| 13 | [lazy_static] | [rust-lang] | Apache-2.0/MIT | Heap-allocated statics | -| 14 | [libc] | [rust-lang] | Apache-2.0/MIT | C library wrapper | -| 15 | [log] | [rust-lang] | Apache-2.0/MIT | Logging facade library | -| 16 | [num-integer] | [rust-num] | Apache-2.0/MIT | `Integer` trait | -| 17 | [num-traits] | [rust-num] | Apache-2.0/MIT | Numeric traits | -| 18 | [redox_syscall] | [redox-os] | MIT | Redox OS syscall API | -| 19 | [rustc-demangle] | [@alexcrichton] | Apache-2.0/MIT | Symbol demangling | -| 20 | [secrecy] | [iqlusion] | Apache-2.0 | Secret-keeping types | -| 21 | [semver] | [@steveklabnik] | Apache-2.0/MIT | Semantic versioning | -| 22 | [semver-parser] | [@steveklabnik] | Apache-2.0/MIT | Parser for semver spec | -| 23 | [serde] | [serde-rs] | Apache-2.0/MIT | Serialization framework | -| 24 | [signal-hook] | [@vorner] | Apache-2.0/MIT | Unix signal handling | -| 25 | [signal-hook-registry] | [@vorner] | Apache-2.0/MIT | Unix signal registry | -| 26 | [termcolor] | [@BurntSushi] | MIT/Unlicense | Terminal color support | -| 27 | [time] | [rust-lang] | Apache-2.0/MIT | Time/date library | -| 28 | [toml] | [@alexcrichton] | Apache-2.0/MIT | TOML parser library | -| 29 | [winapi]§ | [@retep998] | Apache-2.0/MIT | Windows FFI bindings | -| 30 | [winapi-util] | [@BurntSushi] | MIT/Unlicense | Safe winapi wrappers | -| 31 | [wincolor] | [@BurntSushi] | MIT/Unlicense | Windows console color | -| 32 | [zeroize] | [iqlusion] | Apache-2.0/MIT | Zero out sensitive data | +| 10 | [generational-arena] | [@fitzgen] | MPL-2.0 | Component allocator | +| 11 | [gumdrop] | [@Murarth] | Apache-2.0/MIT | Command-line options | +| 12 | [lazy_static] | [rust-lang] | Apache-2.0/MIT | Heap-allocated statics | +| 13 | [libc] | [rust-lang] | Apache-2.0/MIT | C library wrapper | +| 14 | [log] | [rust-lang] | Apache-2.0/MIT | Logging facade library | +| 15 | [num-integer] | [rust-num] | Apache-2.0/MIT | `Integer` trait | +| 16 | [num-traits] | [rust-num] | Apache-2.0/MIT | Numeric traits | +| 17 | [redox_syscall] | [redox-os] | MIT | Redox OS syscall API | +| 18 | [rustc-demangle] | [@alexcrichton] | Apache-2.0/MIT | Symbol demangling | +| 19 | [secrecy] | [iqlusion] | Apache-2.0 | Secret-keeping types | +| 20 | [semver] | [@steveklabnik] | Apache-2.0/MIT | Semantic versioning | +| 21 | [semver-parser] | [@steveklabnik] | Apache-2.0/MIT | Parser for semver spec | +| 22 | [serde] | [serde-rs] | Apache-2.0/MIT | Serialization framework | +| 23 | [signal-hook] | [@vorner] | Apache-2.0/MIT | Unix signal handling | +| 24 | [signal-hook-registry] | [@vorner] | Apache-2.0/MIT | Unix signal registry | +| 25 | [termcolor] | [@BurntSushi] | MIT/Unlicense | Terminal color support | +| 26 | [time] | [rust-lang] | Apache-2.0/MIT | Time/date library | +| 27 | [toml] | [@alexcrichton] | Apache-2.0/MIT | TOML parser library | +| 28 | [winapi]§ | [@retep998] | Apache-2.0/MIT | Windows FFI bindings | +| 29 | [winapi-util] | [@BurntSushi] | MIT/Unlicense | Safe winapi wrappers | +| 30 | [wincolor] | [@BurntSushi] | MIT/Unlicense | Windows console color | +| 31 | [zeroize] | [iqlusion] | Apache-2.0/MIT | Zero out sensitive data | ### Build / Development / Testing Dependencies @@ -138,24 +136,23 @@ default set of features in the application: | 5 | [darling] | [@TedDriggs] | MIT | Nifty attribute parser | | 6 | [darling_core] | [@TedDriggs] | MIT | Attribute parser core | | 7 | [darling_macro] | [@TedDriggs] | MIT | Attribute parser macros | -| 8 | [failure_derive] | [@withoutboats] | Apache-2.0/MIT | `failure` custom derive | -| 9 | [fnv] | [@alexcrichton] | Apache-2.0/MIT | Fast hash function | -| 10 | [gumdrop_derive] | [@Murarth] | Apache-2.0/MIT | Command-line options | -| 11 | [ident_case] | [@TedDriggs] | Apache-2.0/MIT | Case conversion utils | -| 12 | [memchr] | [@BurntSushi] | MIT/Unlicense | Optimized byte search | -| 13 | [proc-macro2] | [@alexcrichton] | Apache-2.0/MIT | Shim for Macros 2.0 API | -| 14 | [quote] | [@dtolnay] | Apache-2.0/MIT | Rust AST to token macro | -| 15 | [regex] | [rust-lang] | Apache-2.0/MIT | Regular expressions | -| 16 | [regex-syntax] | [rust-lang] | Apache-2.0/MIT | Regex syntax impl | -| 17 | [serde_derive] | [serde-rs] | Apache-2.0/MIT | `serde` custom derive | -| 18 | [strsim] | [@dguo] | MIT | String similarity utils | -| 19 | [syn] | [@dtolnay] | Apache-2.0/MIT | Rust source code parser | -| 20 | [synstructure] | [@mystor] | Apache-2.0/MIT | `syn` structure macros | -| 21 | [thread_local] | [@Amanieu] | Apache-2.0/MIT | Per-object thread local | -| 22 | [ucd-util] | [@BurntSushi] | Apache-2.0/MIT | Unicode utilities | -| 23 | [unicode-xid] | [unicode-rs] | Apache-2.0/MIT | Identify valid Unicode | -| 24 | [utf8-ranges] | [@BurntSushi] | MIT/Unlicense | UTF-8 codepoint ranges | -| 25 | [wait-timeout] | [@alexcrichton] | Apache-2.0/MIT | Timeouts for waitpid | +| 8 | [fnv] | [@alexcrichton] | Apache-2.0/MIT | Fast hash function | +| 9 | [gumdrop_derive] | [@Murarth] | Apache-2.0/MIT | Command-line options | +| 10 | [ident_case] | [@TedDriggs] | Apache-2.0/MIT | Case conversion utils | +| 11 | [memchr] | [@BurntSushi] | MIT/Unlicense | Optimized byte search | +| 12 | [proc-macro2] | [@alexcrichton] | Apache-2.0/MIT | Shim for Macros 2.0 API | +| 13 | [quote] | [@dtolnay] | Apache-2.0/MIT | Rust AST to token macro | +| 14 | [regex] | [rust-lang] | Apache-2.0/MIT | Regular expressions | +| 15 | [regex-syntax] | [rust-lang] | Apache-2.0/MIT | Regex syntax impl | +| 16 | [serde_derive] | [serde-rs] | Apache-2.0/MIT | `serde` custom derive | +| 17 | [strsim] | [@dguo] | MIT | String similarity utils | +| 18 | [syn] | [@dtolnay] | Apache-2.0/MIT | Rust source code parser | +| 19 | [synstructure] | [@mystor] | Apache-2.0/MIT | `syn` structure macros | +| 20 | [thread_local] | [@Amanieu] | Apache-2.0/MIT | Per-object thread local | +| 21 | [ucd-util] | [@BurntSushi] | Apache-2.0/MIT | Unicode utilities | +| 22 | [unicode-xid] | [unicode-rs] | Apache-2.0/MIT | Identify valid Unicode | +| 23 | [utf8-ranges] | [@BurntSushi] | MIT/Unlicense | UTF-8 codepoint ranges | +| 24 | [wait-timeout] | [@alexcrichton] | Apache-2.0/MIT | Timeouts for waitpid | ### Dependency Relationships @@ -172,7 +169,7 @@ so you only compile the parts you need. | [arc-swap] | `signals` | [signal-hook-registry] | | [atty] | `terminal` | [color-backtrace] | | [autocfg] | `time` | [num-integer] | -| [backtrace] | - | [failure] | +| [backtrace] | - | [abscissa_core] | | [backtrace-sys] | - | [backtrace] | | [canonical-path] | - | [abscissa_core] | | [cc] | - | [backtrace-sys] | @@ -182,8 +179,6 @@ so you only compile the parts you need. | [darling] | - | [abscissa_derive] | | [darling_core] | - | [darling], [darling_macro] | | [darling_macro] | - | [darling] | -| [failure] | - | [abscissa_core] | -| [failure_derive] | - | [failure] | | [fnv] | - | [darling_core] | | [generational-arena] | `application` | [abscissa_core] | | [gumdrop] | `options` | [abscissa_core] | @@ -195,8 +190,8 @@ so you only compile the parts you need. | [memchr] | `testing` | [aho-corasick] | | [num-integer] | `time` | [chrono] | | [num-traits] | `time` | [chrono], [num-integer] | -| [proc-macro2] | - | [abscissa_derive], [darling], [failure_derive], [quote], [serde_derive], [syn] | -| [quote] | - | [abscissa_derive], [darling], [failure_derive], [gumdrop_derive], [serde_derive] | +| [proc-macro2] | - | [abscissa_derive], [darling], [quote], [serde_derive], [syn] | +| [quote] | - | [abscissa_derive], [darling], [gumdrop_derive], [serde_derive] | | [redox_syscall] | `time` | [time] | | [regex] | `testing` | [abscissa_core] | | [rustc-demangle] | - | [backtrace] | @@ -208,7 +203,7 @@ so you only compile the parts you need. | [signal-hook] | `signals` | [abscissa_core] | | [signal-hook-registry] | `signals` | [signal-hook] | | [strsim] | - | [darling_core] | -| [syn] | - | [abscissa_derive], [darling], [failure_derive], [gumdrop_derive], [serde_derive] | +| [syn] | - | [abscissa_derive], [darling], [gumdrop_derive], [serde_derive] | | [termcolor] | `terminal` | [abscissa_core] | | [thread_local] | `testing` | [regex] | | [time] | `logging` | [chrono] | @@ -361,8 +356,6 @@ read the [CONTRIBUTING.md] and [CODE_OF_CONDUCT.md] files first. [darling]: https://crates.io/crates/darling [darling_core]: https://crates.io/crates/darling_core [darling_macro]: https://crates.io/crates/darling_macro -[failure]: https://crates.io/crates/failure -[failure_derive]: https://crates.io/crates/failure_derive [fnv]: https://crates.io/crates/fnv [generational-arena]: https://github.com/fitzgen/generational-arena [gumdrop]: https://crates.io/crates/gumdrop diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a82099b4..60ba979d 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -19,7 +19,6 @@ keywords = ["abscissa", "cli", "application", "framework", "service"] maintenance = { status = "actively-developed" } [dependencies] -failure = "0.1" gumdrop = "0.7" handlebars = "2" ident_case = "1" diff --git a/cli/src/commands/new.rs b/cli/src/commands/new.rs index ac4f393b..d73e5d57 100644 --- a/cli/src/commands/new.rs +++ b/cli/src/commands/new.rs @@ -3,11 +3,12 @@ #![allow(clippy::never_loop)] use crate::{ + error::{Error, ErrorKind}, + prelude::*, properties::{self, Properties}, template::{Collection, Template}, }; use abscissa_core::{status_err, status_info, status_ok, status_warn, Command, Options, Runnable}; -use failure::{bail, format_err, Error}; use ident_case::RenameRule; use std::{ fs, io, @@ -70,7 +71,7 @@ impl NewCommand { fn app_path(&self) -> Result<&Path, Error> { match &self.app_path { Some(path) => Ok(path.as_ref()), - None => bail!("no app_path given"), + None => fail!(ErrorKind::Path, "no app_path given"), } } @@ -83,15 +84,25 @@ impl NewCommand { status_info!("Exists", "`{}` (application directory)", app_path.display()); return Ok(()); } else { - fatal_error(format_err!( - "destination `{}` already exists", - app_path.display() - )); + fatal_error( + format_err!( + ErrorKind::Path, + "destination `{}` already exists", + app_path.display() + ) + .into(), + ); } } - fs::create_dir(app_path) - .map_err(|e| format_err!("couldn't create {}: {}", app_path.display(), e))?; + fs::create_dir(app_path).map_err(|e| { + format_err!( + ErrorKind::Path, + "couldn't create {}: {}", + app_path.display(), + e + ) + })?; status_ok!( "Created", @@ -116,10 +127,14 @@ impl NewCommand { if self.force { status_warn!("overwriting: {}", output_path.display()) } else { - fatal_error(format_err!( - "file already exists: {}", - output_path.display() - )); + fatal_error( + format_err!( + ErrorKind::Path, + "file already exists: {}", + output_path.display() + ) + .into(), + ); } } @@ -127,11 +142,23 @@ impl NewCommand { let output_dir = output_path.parent().unwrap(); // Create all of the necessary parent directories - fs::create_dir_all(output_dir) - .map_err(|e| format_err!("error creating {}: {}", output_dir.display(), e))?; - - let mut output_file = fs::File::create(&output_path) - .map_err(|e| format_err!("couldn't create {}: {}", output_path.display(), e))?; + fs::create_dir_all(output_dir).map_err(|e| { + format_err!( + ErrorKind::Path, + "error creating {}: {}", + output_dir.display(), + e + ) + })?; + + let mut output_file = fs::File::create(&output_path).map_err(|e| { + format_err!( + ErrorKind::Path, + "couldn't create {}: {}", + output_path.display(), + e + ) + })?; app_template.render(template_file, &app_properties, &mut output_file)?; status_ok!("Created", "new file: {}", output_path_rel.display()); @@ -170,7 +197,9 @@ impl NewCommand { // Ignore errors if git isn't installed Err(e) => { if e.kind() != io::ErrorKind::NotFound { - fatal_error(format_err!("error running git init: {}", e)); + fatal_error( + format_err!(ErrorKind::Git, "error running git init: {}", e).into(), + ); } } } diff --git a/cli/src/error.rs b/cli/src/error.rs new file mode 100644 index 00000000..e1d9fc1f --- /dev/null +++ b/cli/src/error.rs @@ -0,0 +1,92 @@ +//! Error types + +use abscissa_core::error::{BoxError, Context}; +use std::{ + fmt::{self, Display}, + io, + ops::Deref, +}; + +/// Kinds of errors +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ErrorKind { + /// Error in configuration file + Config, + + /// Git-related errors + Git, + + /// Input/output error + Io, + + /// Path-related errors + Path, + + /// Template-related errors + Template, +} + +impl ErrorKind { + /// Create an error context from this error + pub fn context(self, source: impl Into) -> Context { + Context::new(self, Some(source.into())) + } +} + +impl Display for ErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let description = match self { + ErrorKind::Config => "config error", + ErrorKind::Git => "git error", + ErrorKind::Io => "I/O error", + ErrorKind::Path => "bad path", + ErrorKind::Template => "template error", + }; + + f.write_str(description) + } +} + +impl std::error::Error for ErrorKind {} + +/// Error type +#[derive(Debug)] +pub struct Error(Box>); + +impl Deref for Error { + type Target = Context; + + fn deref(&self) -> &Context { + &self.0 + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } +} + +impl From> for Error { + fn from(other: Context) -> Self { + Error(Box::new(other)) + } +} + +impl From for Error { + fn from(err: handlebars::RenderError) -> Self { + ErrorKind::Template.context(err).into() + } +} + +impl From for Error { + fn from(err: io::Error) -> Self { + ErrorKind::Io.context(err).into() + } +} diff --git a/cli/src/lib.rs b/cli/src/lib.rs index 88b754d3..a8d25144 100644 --- a/cli/src/lib.rs +++ b/cli/src/lib.rs @@ -14,6 +14,7 @@ pub mod application; pub mod commands; pub mod config; +pub mod error; pub mod prelude; pub mod properties; pub mod template; diff --git a/cli/src/prelude.rs b/cli/src/prelude.rs index 9cc99c5f..17c948af 100644 --- a/cli/src/prelude.rs +++ b/cli/src/prelude.rs @@ -5,7 +5,10 @@ pub use abscissa_core::{Application, Command, Runnable}; /// Error macros -pub use abscissa_core::error::macros::{}; +pub use abscissa_core::{ensure, fail, fatal, format_err}; /// Logging macros pub use abscissa_core::log::{debug, error, info, log, log_enabled, trace, warn}; + +/// Status macros +pub use abscissa_core::{status_err, status_info, status_ok, status_warn}; diff --git a/cli/src/template/collection.rs b/cli/src/template/collection.rs index d1979ff5..dca4648f 100644 --- a/cli/src/template/collection.rs +++ b/cli/src/template/collection.rs @@ -2,8 +2,11 @@ //! generate an Abscissa application. use super::{iter::Iter, Template}; -use crate::{prelude::*, properties::Properties}; -use failure::{format_err, Error}; +use crate::{ + error::{Error, ErrorKind}, + prelude::*, + properties::Properties, +}; use handlebars::Handlebars; use std::io; @@ -63,8 +66,14 @@ impl Collection { for (name, contents) in template_files { debug!("registering template: {}", name); - hbs.register_template_string(name, contents) - .map_err(|e| format_err!("couldn't register template '{}': {}", name, e))?; + hbs.register_template_string(name, contents).map_err(|e| { + format_err!( + ErrorKind::Template, + "couldn't register template '{}': {}", + name, + e + ) + })?; } Ok(Collection(hbs)) @@ -100,7 +109,7 @@ impl Collection { template .inner .render(&self.0, &ctx, &mut render_context, &mut output) - .map_err(|e| format_err!("render error: {}", e)) + .map_err(|e| format_err!(ErrorKind::Template, "render error: {}", e).into()) } } diff --git a/cli/template/Cargo.toml.hbs b/cli/template/Cargo.toml.hbs index 26e6a3b5..7b8a4f9d 100644 --- a/cli/template/Cargo.toml.hbs +++ b/cli/template/Cargo.toml.hbs @@ -5,7 +5,6 @@ version = "{{version}}" edition = "{{edition}}" [dependencies] -failure = "0.1" gumdrop = "0.7" lazy_static = "1" serde = { version = "1", features = ["serde_derive"] } diff --git a/cli/template/src/error.rs.hbs b/cli/template/src/error.rs.hbs index 8da5180e..2685a73e 100644 --- a/cli/template/src/error.rs.hbs +++ b/cli/template/src/error.rs.hbs @@ -1,47 +1,74 @@ //! Error types -use abscissa_core::err; -use failure::Fail; -use std::{fmt, io, ops::Deref}; - -/// Error type -#[derive(Debug)] -pub struct Error(abscissa_core::Error); +use abscissa_core::error::{BoxError, Context}; +use std::{ + fmt::{self, Display}, + io, + ops::Deref, +}; /// Kinds of errors -#[derive(Copy, Clone, Eq, PartialEq, Debug, Fail)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] pub enum ErrorKind { /// Error in configuration file - #[fail(display = "config error")] Config, /// Input/output error - #[fail(display = "I/O error")] Io, } +impl ErrorKind { + /// Create an error context from this error + pub fn context(self, source: impl Into) -> Context { + Context::new(self, Some(source.into())) + } +} + +impl Display for ErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let description = match self { + ErrorKind::Config => "config error", + ErrorKind::Io => "I/O error", + }; + + f.write_str(description) + } +} + +impl std::error::Error for ErrorKind {} + +/// Error type +#[derive(Debug)] +pub struct Error(Box>); + impl Deref for Error { - type Target = abscissa_core::Error; + type Target = Context; - fn deref(&self) -> &abscissa_core::Error { + fn deref(&self) -> &Context { &self.0 } } -impl fmt::Display for Error { +impl Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } -impl From> for Error { - fn from(other: abscissa_core::Error) -> Self { - Error(other) +impl std::error::Error for Error { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } +} + +impl From> for Error { + fn from(context: Context) -> Self { + Error(Box::new(context)) } } impl From for Error { - fn from(other: io::Error) -> Self { - err!(ErrorKind::Io, other).into() + fn from(err: io::Error) -> Self { + ErrorKind::Io.context(err).into() } } diff --git a/cli/template/src/prelude.rs.hbs b/cli/template/src/prelude.rs.hbs index f2333599..dc8bd146 100644 --- a/cli/template/src/prelude.rs.hbs +++ b/cli/template/src/prelude.rs.hbs @@ -8,7 +8,7 @@ pub use crate::application::{app_config, app_reader, app_writer}; pub use abscissa_core::{Application, Command, Runnable}; /// Error macros -pub use abscissa_core::{ensure, fail, fatal}; +pub use abscissa_core::{ensure, fail, fatal, format_err}; /// Logging macros pub use abscissa_core::log::{debug, error, info, log, log_enabled, trace, warn}; diff --git a/core/Cargo.toml b/core/Cargo.toml index 7510c2ec..416fe160 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -20,10 +20,10 @@ maintenance = { status = "actively-developed" } [dependencies] abscissa_derive = { version = "0.4", path = "../derive" } +backtrace = "0.3" canonical-path = "2" chrono = { version = "0.4", optional = true, features = ["serde"] } color-backtrace = { version = "0.3", optional = true, default-features = false } -failure = "0.1" generational-arena = { version = "0.2", optional = true } gumdrop = { version = "0.7", optional = true } lazy_static = "1" @@ -41,10 +41,31 @@ libc = { version = "0.2", optional = true } signal-hook = { version = "0.1", optional = true } [features] -default = ["application", "signals", "secrets", "testing", "time"] -application = ["config", "generational-arena", "logging", "options", "semver/serde", "terminal"] -config = ["secrets", "serde", "terminal", "toml"] -gimli-backtrace = ["color-backtrace/gimli-symbolize"] +default = [ + "application", + "signals", + "secrets", + "testing", + "time" +] +application = [ + "config", + "generational-arena", + "logging", + "options", + "semver/serde", + "terminal" +] +config = [ + "secrets", + "serde", + "terminal", + "toml" +] +gimli-backtrace = [ + "backtrace/gimli-symbolize", + "color-backtrace/gimli-symbolize" +] logging = ["log"] options = ["gumdrop"] secrets = ["secrecy"] diff --git a/core/src/application.rs b/core/src/application.rs index 7dcc2da9..bd53db2d 100644 --- a/core/src/application.rs +++ b/core/src/application.rs @@ -13,12 +13,13 @@ use crate::{ command::Command, component::Component, config::{Config, Configurable}, - error::{FrameworkError, FrameworkErrorKind::*}, logging::{self, Logging}, path::{AbsPathBuf, ExePath, RootPath}, runnable::Runnable, shutdown::Shutdown, terminal::{component::Terminal, ColorChoice}, + FrameworkError, + FrameworkErrorKind::*, Version, }; use std::{env, path::Path, process, vec}; @@ -55,7 +56,7 @@ pub trait Application: Default + Sized + 'static { // Initialize application let mut app = Self::default(); - app.init(&command).unwrap_or_else(|e| fatal_error!(&app, e)); + app.init(&command).unwrap_or_else(|e| fatal_error(&app, &e)); app_lock.set(app); // Run the command @@ -128,17 +129,13 @@ pub trait Application: Default + Sized + 'static { /// /// Returns an error if the configuration could not be loaded. fn load_config(&mut self, path: &Path) -> Result { - let canonical_path = AbsPathBuf::canonicalize(path) - .map_err(|e| err!(ConfigError, "invalid path '{}': {}", path.display(), e))?; - - Self::Cfg::load_toml_file(&canonical_path).map_err(|e| { - err!( - ConfigError, - "error loading config from '{}': {}", - canonical_path.display(), - e - ) - }) + let canonical_path = AbsPathBuf::canonicalize(path).map_err(|e| { + let path_error = PathError { + name: Some(path.into()), + }; + FrameworkError::from(ConfigError.context(path_error)) + })?; + Self::Cfg::load_toml_file(&canonical_path) } /// Name of this application as a string. @@ -155,7 +152,7 @@ pub trait Application: Default + Sized + 'static { fn version(&self) -> Version { Self::Cmd::version() .parse::() - .unwrap_or_else(|e| fatal_error!(self, e)) + .unwrap_or_else(|e| fatal_error(self, &e)) } /// Authors of this application. @@ -182,10 +179,11 @@ pub trait Application: Default + Sized + 'static { /// Shut down this application gracefully, exiting with success. fn shutdown(&mut self, shutdown: Shutdown) -> ! { - match self.state().components.shutdown(self, shutdown) { - Ok(()) => process::exit(0), - Err(e) => fatal_error(self, &e.into()), + if let Err(e) = self.state().components.shutdown(self, shutdown) { + fatal_error(self, &e) } + + process::exit(0); } } diff --git a/core/src/application/exit.rs b/core/src/application/exit.rs index 16030748..2c088425 100644 --- a/core/src/application/exit.rs +++ b/core/src/application/exit.rs @@ -1,15 +1,10 @@ //! Default exit handlers for Abscissa applications -use failure::Error; -use std::process; - use super::{Application, Component}; +use std::{error::Error, process}; /// Print a fatal error message and exit -pub fn fatal_error(app: &A, err: &Error) -> ! -where - A: Application, -{ +pub fn fatal_error(app: &impl Application, err: &dyn Error) -> ! { status_err!("{} fatal error: {}", app.name(), err); process::exit(1) } diff --git a/core/src/component.rs b/core/src/component.rs index 75f7591f..0c533ae9 100644 --- a/core/src/component.rs +++ b/core/src/component.rs @@ -12,7 +12,7 @@ pub use self::{handle::Handle, id::Id, registry::Registry}; #[doc(hidden)] pub use abscissa_derive::Component; -use crate::{application::Application, error::FrameworkError, shutdown::Shutdown, Version}; +use crate::{application::Application, shutdown::Shutdown, FrameworkError, Version}; use std::{any::Any, cmp::Ordering, fmt::Debug, slice::Iter}; /// Application components. diff --git a/core/src/component/registry.rs b/core/src/component/registry.rs index ebf7aa6b..f64c3e2b 100644 --- a/core/src/component/registry.rs +++ b/core/src/component/registry.rs @@ -7,8 +7,9 @@ pub use self::iter::{Iter, IterMut}; use super::{handle::Handle, id::Id, Component}; use crate::{ application::{self, Application}, - error::{FrameworkError, FrameworkErrorKind::ComponentError}, shutdown::Shutdown, + FrameworkError, + FrameworkErrorKind::ComponentError, }; use generational_arena::{Arena, Index}; use std::{any::TypeId, borrow::Borrow, collections::BTreeMap}; diff --git a/core/src/config.rs b/core/src/config.rs index 304eaac6..cf972036 100644 --- a/core/src/config.rs +++ b/core/src/config.rs @@ -9,8 +9,9 @@ pub use self::{configurable::Configurable, overrides::Override, reader::Reader}; pub use abscissa_derive::Config; use crate::{ - error::{FrameworkError, FrameworkErrorKind::ConfigError}, path::AbsPath, + FrameworkError, + FrameworkErrorKind::{ConfigError, IoError, PathError}, }; use serde::de::DeserializeOwned; use std::{fmt::Debug, fs::File, io::Read}; @@ -29,12 +30,12 @@ pub trait Config: Debug + Default + DeserializeOwned { P: AsRef, { let mut file = File::open(path.as_ref()).map_err(|e| { - err!( - ConfigError, - "couldn't open {}: {}", - path.as_ref().display(), - e - ) + let io_error = IoError.context(e); + let path_error = PathError { + name: Some(path.as_ref().as_path().into()), + } + .context(io_error); + ConfigError.context(path_error) })?; let mut toml_string = String::new(); diff --git a/core/src/config/configurable.rs b/core/src/config/configurable.rs index b67bbf12..c33e6a7e 100644 --- a/core/src/config/configurable.rs +++ b/core/src/config/configurable.rs @@ -1,7 +1,7 @@ //! Configuration loader use super::Config; -use crate::error::FrameworkError; +use crate::FrameworkError; use std::path::PathBuf; /// Command type with which a configuration file is associated diff --git a/core/src/error.rs b/core/src/error.rs index 5fb509c6..c8f9a69e 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -2,74 +2,13 @@ #[macro_use] pub mod macros; -mod framework; -pub use self::framework::{FrameworkError, FrameworkErrorKind}; -pub use failure::{Backtrace, Context, Fail}; -use std::fmt::{self, Display}; +pub mod context; +pub mod framework; +pub mod message; -/// Error types used by this library, generic around `Kind`s -#[derive(Debug)] -pub struct Error -where - Kind: Fail + Clone + Display + Eq + PartialEq, -{ - /// Contextual information about the error - inner: Context, +pub use self::{context::Context, message::Message}; - /// Description of the error providing additional information - description: Option, -} - -impl Error -where - Kind: Fail + Clone + Display + Eq + PartialEq, -{ - /// Create a new error from the given context object and description - pub fn new(into_context: C, description: Option) -> Self - where - C: Into>, - { - let inner = into_context.into(); - Self { inner, description } - } - - /// Obtain the error's `Kind` - pub fn kind(&self) -> &Kind { - self.inner.get_context() - } -} - -impl Display for Error -where - Kind: Fail + Clone + Display + Eq + PartialEq, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self.description { - Some(ref desc) => write!(f, "{}: {}", self.kind(), desc), - None => write!(f, "{}", self.kind()), - } - } -} - -impl Fail for Error -where - Kind: Fail + Clone + Display + Eq + PartialEq, -{ - fn cause(&self) -> Option<&dyn Fail> { - self.inner.cause() - } - - fn backtrace(&self) -> Option<&Backtrace> { - self.inner.backtrace() - } -} - -impl From for Error -where - Kind: Fail + Clone + Display + Eq + PartialEq, -{ - fn from(kind: Kind) -> Self { - Error::new(kind, None) - } -} +/// Box containing a thread-safe + `'static` error suitable for use as a +/// as an `std::error::Error::source` +pub type BoxError = Box; diff --git a/core/src/error/context.rs b/core/src/error/context.rs new file mode 100644 index 00000000..3847b4da --- /dev/null +++ b/core/src/error/context.rs @@ -0,0 +1,86 @@ +//! Error contexts + +use super::BoxError; +use backtrace::Backtrace; +use std::fmt::{self, Debug, Display}; + +/// Error context +#[derive(Debug)] +pub struct Context +where + Kind: Clone + Debug + Display + Eq + PartialEq + Into, +{ + /// Kind of error + kind: Kind, + + /// Backtrace where error occurred + backtrace: Option, + + /// Source of the error + source: Option, +} + +impl Context +where + Kind: Clone + Debug + Display + Eq + PartialEq + Into, +{ + /// Create a new error context + pub fn new(kind: Kind, source: Option) -> Self { + let backtrace = Some(Backtrace::new_unresolved()); + Context { + kind, + backtrace, + source, + } + } + + /// Get the kind of error + pub fn kind(&self) -> &Kind { + &self.kind + } + + /// Get the backtrace associated with this error (if available) + pub fn backtrace(&self) -> Option<&Backtrace> { + self.backtrace.as_ref() + } + + /// Extract the backtrace from the context, allowing it to be resolved. + pub fn into_backtrace(self) -> Option { + self.backtrace + } +} + +impl Display for Context +where + Kind: Clone + Debug + Display + Eq + PartialEq + Into, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", &self.kind)?; + + if let Some(ref source) = self.source { + write!(f, ": {}", source)?; + } + + Ok(()) + } +} + +impl From for Context +where + Kind: Clone + Debug + Display + Eq + PartialEq + Into, +{ + fn from(kind: Kind) -> Context { + Self::new(kind, None) + } +} + +impl std::error::Error for Context +where + Kind: Clone + Debug + Display + Eq + PartialEq + Into, +{ + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.source + .as_ref() + .map(|source| source.as_ref() as &(dyn std::error::Error + 'static)) + } +} diff --git a/core/src/error/framework.rs b/core/src/error/framework.rs index 06b21d47..4a548d74 100644 --- a/core/src/error/framework.rs +++ b/core/src/error/framework.rs @@ -1,67 +1,124 @@ -use super::Error; -use failure::Fail; -use std::io; +//! Framework error types + +use super::{context::Context, BoxError}; +use std::{ + fmt::{self, Display}, + io, + ops::Deref, + path::PathBuf, +}; + +/// Abscissa-internal framework errors +#[derive(Debug)] +pub struct FrameworkError(Box>); + +impl Deref for FrameworkError { + type Target = Context; + + fn deref(&self) -> &Context { + &self.0 + } +} + +impl Display for FrameworkError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.fmt(f) + } +} + +impl From> for FrameworkError { + fn from(context: Context) -> Self { + FrameworkError(Box::new(context)) + } +} + +impl std::error::Error for FrameworkError { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { + self.0.source() + } +} /// Types of errors which occur internally within the framework -#[derive(Fail, Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, Eq, PartialEq)] pub enum FrameworkErrorKind { /// Errors relating to components #[cfg(feature = "application")] - #[fail(display = "component error")] ComponentError, /// Error reading configuration file - #[fail(display = "config error")] ConfigError, /// I/O operation failed - #[fail(display = "I/O operation failed")] IoError, /// Couldn't parse the given value - #[fail(display = "parse error")] ParseError, /// Errors associated with filesystem paths - #[fail(display = "path error")] - PathError, + PathError { + /// Name of the affected path (if applicable) + name: Option, + }, /// Errors occurring in subprocess - #[fail(display = "subprocess error")] ProcessError, /// Errors involving signals - #[fail(display = "signal error")] SignalError, /// Errors involving multithreading - #[fail(display = "thread error")] ThreadError, /// Timeout performing operation - #[fail(display = "operation timed out")] TimeoutError, } -impl From for FrameworkError { - fn from(err: io::Error) -> Self { - err!(FrameworkErrorKind::IoError, err) +impl FrameworkErrorKind { + /// Create an error context from this error + pub fn context(self, source: impl Into) -> Context { + Context::new(self, Some(source.into())) + } + + /// Get a message to display for this error + pub fn msg(&self) -> &'static str { + match self { + #[cfg(feature = "application")] + FrameworkErrorKind::ComponentError => "component error", + FrameworkErrorKind::ConfigError => "config error", + FrameworkErrorKind::IoError => "I/O operation failed", + FrameworkErrorKind::ParseError => "parse error", + FrameworkErrorKind::PathError { .. } => "path error", + FrameworkErrorKind::ProcessError => "subprocess error", + FrameworkErrorKind::SignalError => "signal error", + FrameworkErrorKind::ThreadError => "thread error", + FrameworkErrorKind::TimeoutError => "operation timed out", + } } } -#[cfg(feature = "term")] -impl From for FrameworkError { - fn from(err: term::Error) -> Self { - err!(FrameworkErrorKind::IoError, err) +impl Display for FrameworkErrorKind { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.msg())?; + + if let FrameworkErrorKind::PathError { name: Some(name) } = self { + write!(f, ": {}", name.display())?; + } + + Ok(()) + } +} + +impl std::error::Error for FrameworkErrorKind {} + +impl From for FrameworkError { + fn from(err: io::Error) -> Self { + FrameworkErrorKind::IoError.context(err).into() } } #[cfg(feature = "toml")] impl From for FrameworkError { fn from(err: toml::de::Error) -> Self { - err!(FrameworkErrorKind::ParseError, err) + FrameworkErrorKind::ParseError.context(err).into() } } - -/// Abscissa-internal framework errors -pub type FrameworkError = Error; diff --git a/core/src/error/macros.rs b/core/src/error/macros.rs index f5fa2995..94836e18 100644 --- a/core/src/error/macros.rs +++ b/core/src/error/macros.rs @@ -10,22 +10,22 @@ /// Create a new error (of a given kind) with a formatted message #[macro_export] -macro_rules! err { - ($kind:path, $msg:expr) => { - $crate::error::Error::new($crate::error::Context::new($kind), Some($msg.to_string())) +macro_rules! format_err { + ($kind:expr, $msg:expr) => { + $kind.context($crate::error::Message::new($msg)) }; - ($kind:path, $fmt:expr, $($arg:tt)+) => { - err!($kind, &format!($fmt, $($arg)+)) + ($kind:expr, $fmt:expr, $($arg:tt)+) => { + format_err!($kind, &format!($fmt, $($arg)+)) }; } /// Create and return an error with a formatted message #[macro_export] macro_rules! fail { - ($kind:path, $msg:expr) => { - return Err(err!($kind, $msg).into()); + ($kind:expr, $msg:expr) => { + return Err(format_err!($kind, $msg).into()); }; - ($kind:path, $fmt:expr, $($arg:tt)+) => { + ($kind:expr, $fmt:expr, $($arg:tt)+) => { fail!($kind, &format!($fmt, $($arg)+)); }; } @@ -33,12 +33,12 @@ macro_rules! fail { /// Ensure a condition holds, returning an error if it doesn't (ala assert) #[macro_export] macro_rules! ensure { - ($cond:expr, $kind:path, $msg:expr) => { + ($cond:expr, $kind:expr, $msg:expr) => { if !($cond) { - return Err(err!($kind, $msg).into()); + return Err(format_err!($kind, $msg).into()); } }; - ($cond:expr, $kind:path, $fmt:expr, $($arg:tt)+) => { + ($cond:expr, $kind:expr, $fmt:expr, $($arg:tt)+) => { ensure!($cond, $kind, format!($fmt, $($arg)+)) }; } @@ -53,18 +53,9 @@ macro_rules! ensure { #[macro_export] macro_rules! fatal { ($app:expr, $msg:expr) => { - fatal_error!($app, ::failure::err_msg($smg)) + $crate::application::exit::fatal_error($app, $crate::error::Message::new($msg)) }; ($app:expr, $fmt:expr, $($arg:tt)+) => { fatal!($app, format!($fmt, $($arg:tt)+)) }; } - -/// Terminate the application with a fatal error, running Abscissa's shutdown hooks. -#[macro_export] -macro_rules! fatal_error { - ($app:expr, $err:expr) => {{ - let err: ::failure::Error = $err.into(); - $crate::application::exit::fatal_error($app, &err) - }}; -} diff --git a/core/src/error/message.rs b/core/src/error/message.rs new file mode 100644 index 00000000..8a144ed0 --- /dev/null +++ b/core/src/error/message.rs @@ -0,0 +1,42 @@ +//! Error messages + +use std::{ + error::Error, + fmt::{self, Display}, + string::ToString, +}; + +/// Error message type: provide additional context with a string. +/// +/// This is generally discouraged whenever possible as it will complicate +/// future I18n support. However, it can be useful for things with +/// language-independent string representations for error contexts. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct Message(String); + +impl Message { + /// Create a new error message + pub fn new(msg: impl ToString) -> Self { + Message(msg.to_string()) + } +} + +impl AsRef for Message { + fn as_ref(&self) -> &str { + self.0.as_ref() + } +} + +impl Display for Message { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str(self.as_ref()) + } +} + +impl Error for Message {} + +impl From for Message { + fn from(string: String) -> Message { + Message(string) + } +} diff --git a/core/src/lib.rs b/core/src/lib.rs index f98d226a..d2cce9d8 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -10,8 +10,7 @@ //! top of the [gumdrop] crate. //! - **configuration**: TOML configuration file parsing on application-defined //! configuration structures which can be dynamically updated at runtime. -//! - **error handling**: generic `Error` type based on the `failure` crate, and a -//! unified error-handling subsystem. +//! - **error handling**: unified error-handling subsystem with generic errors. //! - **logging**: uses the `log` crate to provide application-level logging. //! - **secrets management**: the (optional) `secrets` module includes a `Secret` //! type which derives serde's `Deserialize` and can be used to represent secret @@ -145,7 +144,7 @@ pub use gumdrop::Options; #[cfg(feature = "config")] pub use crate::config::{Config, Configurable}; -pub use crate::error::{Error, Fail, FrameworkError, FrameworkErrorKind}; +pub use crate::error::framework::{FrameworkError, FrameworkErrorKind}; pub use crate::runnable::Runnable; #[cfg(feature = "application")] pub use crate::{ diff --git a/core/src/path.rs b/core/src/path.rs index 6d51c6c9..ecbcb677 100644 --- a/core/src/path.rs +++ b/core/src/path.rs @@ -8,7 +8,7 @@ pub use canonical_path::{CanonicalPath as AbsPath, CanonicalPathBuf as AbsPathBu // Just in case anyone gets confused why `Path` is private pub use std::path::{Path, PathBuf}; -use crate::error::FrameworkError; +use crate::FrameworkError; /// Name of the application's secrets directory pub(crate) const SECRETS_DIR: &str = "secrets"; diff --git a/core/src/signal.rs b/core/src/signal.rs index 3870046c..d01fcf1d 100644 --- a/core/src/signal.rs +++ b/core/src/signal.rs @@ -2,11 +2,8 @@ use crate::{ application::{self, Application}, - error::{ - FrameworkError, - FrameworkErrorKind::{SignalError, ThreadError}, - }, - thread, + thread, FrameworkError, + FrameworkErrorKind::{SignalError, ThreadError}, }; use libc::c_int; use signal_hook::iterator::Signals; @@ -101,7 +98,7 @@ where let mut app = app_lock.write(); let thread_name = thread::Name::new("abscissa::signal"); let signals = Signals::new(signals.into_iter().map(|s| s.number() as c_int)) - .map_err(|e| err!(ThreadError, "{}", e))?; + .map_err(|e| format_err!(ThreadError, "{}", e))?; app.state_mut() .threads diff --git a/core/src/terminal/status.rs b/core/src/terminal/status.rs index 74ba709f..d8dd36c4 100644 --- a/core/src/terminal/status.rs +++ b/core/src/terminal/status.rs @@ -44,7 +44,7 @@ //! ``` use super::stream::{STDERR, STDOUT}; -use crate::error::FrameworkError; +use crate::FrameworkError; use std::io::Write; use termcolor::{Color, ColorSpec, StandardStream, WriteColor}; diff --git a/core/src/testing/process.rs b/core/src/testing/process.rs index 39f982bc..de22ff33 100644 --- a/core/src/testing/process.rs +++ b/core/src/testing/process.rs @@ -8,7 +8,7 @@ pub use self::{ streams::{OutputStream, Stderr, Stdout}, }; -use crate::error::{ +use crate::{ FrameworkError, FrameworkErrorKind::{ProcessError, TimeoutError}, }; @@ -87,7 +87,7 @@ impl<'cmd> Process<'cmd> { match self.child.wait_timeout(self.timeout)? { Some(status) => { let code = status.code().ok_or_else(|| { - err!(ProcessError, "no exit status returned from subprocess!") + format_err!(ProcessError, "no exit status returned from subprocess!") })?; Ok(ExitStatus::new(code, self.guard)) diff --git a/core/src/thread.rs b/core/src/thread.rs index 4f9d17be..297dce39 100644 --- a/core/src/thread.rs +++ b/core/src/thread.rs @@ -9,7 +9,7 @@ mod name; pub use self::{manager::Manager, name::Name}; use self::kill_switch::KillSwitch; -use crate::error::{FrameworkError, FrameworkErrorKind::ThreadError}; +use crate::{FrameworkError, FrameworkErrorKind::ThreadError}; use std::{io, sync::Arc, thread}; /// Threads spawned and managed by Abscissa @@ -70,7 +70,7 @@ where // Wait for the other thread to exit self.handle .join() - .map_err(|e| err!(ThreadError, "{:?}", e))?; + .map_err(|e| format_err!(ThreadError, "{:?}", e))?; Ok(()) } diff --git a/core/src/thread/manager.rs b/core/src/thread/manager.rs index 55df4f43..e6988e45 100644 --- a/core/src/thread/manager.rs +++ b/core/src/thread/manager.rs @@ -1,7 +1,7 @@ //! Thread manager. use super::{Name, Thread}; -use crate::error::{FrameworkError, FrameworkErrorKind::ThreadError}; +use crate::{FrameworkError, FrameworkErrorKind::ThreadError}; use std::collections::HashMap; /// Thread manager that tracks threads spawned by the application and handles