From ac5393b1e5d8a0e5ddca4df98e64b5fbb142357c Mon Sep 17 00:00:00 2001 From: Brian Anderson Date: Wed, 6 Jul 2016 11:32:32 -0700 Subject: [PATCH] Initial commit --- .gitattributes | 4 + .gitignore | 3 + .travis.yml | 25 +++ Cargo.toml | 17 ++ README.md | 231 ++++++++++++++++++++ src/lib.rs | 504 +++++++++++++++++++++++++++++++++++++++++++ src/quick_error.rs | 521 +++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1305 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .travis.yml create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 src/lib.rs create mode 100644 src/quick_error.rs diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..c531d7a5c --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +*.rs text eol=lf +*.lock text eol=lf +*.txt text eol=lf +*.toml text eol=lf \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..98171f1fe --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*~ +target/ +Cargo.lock \ No newline at end of file diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 000000000..f0095df3d --- /dev/null +++ b/.travis.yml @@ -0,0 +1,25 @@ +sudo: false +language: rust +addons: + apt: + packages: + - libcurl4-openssl-dev + - libelf-dev + - libdw-dev +rust: +- stable +before_script: +- | + pip install 'travis-cargo<0.2' --user && + export PATH=$HOME/.local/bin:$PATH +script: +- | + travis-cargo build && + travis-cargo test && + travis-cargo --only stable doc +after_success: +- travis-cargo --only stable doc-upload +env: + global: + - secure: TODO + - secure: ncxJbvJM1vCZfcEftjsFKJMxxhKLgWKaR8Go9AMo0VB5fB2XVW/6NYO5bQEEYpOf1Nc/+2FbI2+Dkz0S/mJpUcNSfBgablCHgwU2sHse7KsoaqfHj2mf1E3exjzSHoP96hPGicC5zAjSXFjCgJPOUSGqqRaJ7z5AsJLhJT6LuK7QpvwPBZzklUN8T+n1sVmws8TNmRIbaniq/q6wYHANHcy6Dl59dx4sKwniUGiZdUhCiddVpoxbECSxc0A8mN2pk7/aW+WGxK3goBs5ZF7+JXF318F62pDcXQmR5CX6WdpenIcJ25g1Vg1WhQ4Ifpe17CN0bfxV8ShuzrQUThCDMffZCo9XySBtODdEowwK1UIpjnFLfIxjOs45Cd8o3tM2j0CfvtnjOz6BCdUU0qiwNPPNx0wFkx3ZiOfSh+FhBhvyPM12HN2tdN0esgVBItFmEci+sSIIXqjVL6DNiu5zTjbu0bs6COwlUWdmL6vmsZtq5tl7Cno9+C3szxRVAkShGydd04l9NYjqNEzTa1EPG50OsnVRKGdRiFzSxhc3BWExNKvcQ4v867t6/PpPkW6s4oXmYI3+De+8O7ExWc6a4alcrDXKlMs5fCb5Pcd4Ju9kowcjkoJo5yf2wW3Ox5R8SJpaEEpvyhx5O/qtIxjhHNzeo8Wsr/6gdNDv20r91TI= diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..f552805c6 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,17 @@ +[package] + +name = "error-chain" +version = "0.2.1" +authors = [ "Brian Anderson ", + "Paul Colomiets ", + "Colin Kiegel "] +description = "Yet another error boilerplate library" + +documentation = "http://brson.github.io/error-chain/index.html" +homepage = "https://github.com/brson/error-chain" +repository = "https://github.com/brson/error-chain" + +license = "MIT OR Apache-2.0" + +[dependencies] +backtrace = "0.2.1" diff --git a/README.md b/README.md new file mode 100644 index 000000000..cc2418885 --- /dev/null +++ b/README.md @@ -0,0 +1,231 @@ +# error-chain - Consistent error handling for Rust + +The [error-chain crate](https://crates.io/crates/error-chain) +([docs](http://brson.github.io/error-chain/index.html)) is a new crate +for dealing with Rust error boilerplate. It provides a few unique +features: + +* No error is ever discarded. This library primarily makes it easy to + "chain" errors with the `chain_err` method. +* Introducing new errors is trivial. Simple errors can be introduced + at the error site with just a string. +* Errors create and propagate backtraces. + +I think the lack of the above are widespread problems with Rust error +handling, so I'm interested to hear what people think about this +solution. It is inspired by +[quick-error](https://github.com/tailhook/quick-error) (and in fact +includes a hacked up version of it for internal use) as well as +Cargo's internal error handling techniques. This library is used by +[rustup](https://github.com/rust-lang-nursery/rustup.rs) for error +handling. + +One note about usage that isn't included in the docs: the +`chain_error!` macro recurses deeply, so you'll probably need to use +the little-known `#![recursion_limit = "1024"]` macro on crates that +import it. + +For detailed usage information read [the +docs](http://brson.github.io/error-chain/index.html), +which are reproduced in part below. + +## Declaring error types + +Generally, you define one family of error types per crate, though it's +also perfectly fine to define error types on a finer-grained basis, +such as per module. + +Assuming you are using crate-level error types, typically you will +define an `errors` module and inside it call `error_chain!`: + +```rust +// Define the error types and conversions for this crate +error_chain! { + // The type defined for this error. These are the conventional + // and recommended names, but they can be arbitrarily chosen. + types { + Error, ErrorKind, ChainErr, Result; + } + + // Automatic conversions between this error chain and other + // error chains. In this case, it will e.g. generate an + // `ErrorKind` variant called `Dist` which in turn contains + // the `rustup_dist::ErrorKind`, with conversions from + // `rustup_dist::Error`. + // + // This section can be empty. + links { + rustup_dist::Error, rustup_dist::ErrorKind, Dist; + rustup_utils::Error, rustup_utils::ErrorKind, Utils; + } + + // Automatic conversions between this error chain and other + // error types not defined by the `error_chain!`. These will be + // boxed as the error cause and wrapped in a new error with, + // in this case, the `ErrorKind::Temp` variant. + // + // This section can be empty. + foreign_links { + temp::Error, Temp, + "temporary file error"; + } + + // Define additional `ErrorKind` variants. The syntax here is + // the same as `quick_error!`, but the `from()` and `cause()` + // syntax is not supported. + errors { + InvalidToolchainName(t: String) { + description("invalid toolchain name") + display("invalid toolchain name: '{}'", t) + } + } +} +``` + +This populates the the module with a number of definitions, the most +important of which are the `Error` type and the `ErrorKind` type. They +look something like the following: + +```rust +use std::error::Error as StdError; +use std::sync::Arc; + +#[derive(Debug)] +pub struct Error(pub ErrorKind, + pub Option>, + pub Arc); + +impl Error { + pub fn kind(&self) -> &ErrorKind { ... } + pub fn into_kind(self) -> ErrorKind { ... } + pub fn iter(&self) -> error_chain::ErrorChainIter { ... } + pub fn backtrace(&self) -> &error_chain::Backtrace { ... } +} + +impl StdError for Error { ... } +impl Display for Error { ... } + +#[derive(Debug)] +pub enum ErrorKind { + Msg(String), + Dist(rustup_dist::ErrorKind), + Utils(rustup_utils::ErrorKind), + Temp, + InvalidToolchainName(String), +} +``` + +This is the basic error structure. You can see that `ErrorKind` has +been populated in a variety of ways. All `ErrorKind`s get a `Msg` +variant for basic errors. When strings are converted to `ErrorKind`s +they become `ErrorKind::Msg`. The "links" defined in the macro are +expanded to `Dist` and `Utils` variants, and the "foreign links" to +the `Temp` variant. + +Both types come with a variety of `From` conversions as well: `Error` +can be created from `ErrorKind`, from `&str` and `String`, and from +the "link" and "foreign_link" error types. `ErrorKind` can be created +from the corresponding `ErrorKind`s of the link types, as wall as from +`&str` and `String`. + +`into()` and `From::from` are used heavily to massage types into the +right shape. Which one to use in any specific case depends on the +influence of type inference, but there are some patterns that arise +frequently. + +## Chaining errors + +This is the focus of the crate's design. To extend the error chain: + +``` +use errors::ChainErr; +try!(do_something().chain_err(|| "something went wrong")); +``` + +`chain_err` can be called on any `Result` type where the contained +error type implements `std::error::Error + Send + 'static`. If the +`Result` is an `Err` then `chain_err` evaluates the closure, which +returns *some type that can be converted to `ErrorKind`*, boxes the +original error to store as the cause, then returns a new error +containing the original error. + +The above example turns a string into an error, but you could also write e.g. + +``` +try!(do_something().chain_err(|| ErrorKind::Foo)); +``` + +## Returning new errors + +Introducing new error chains, with a string message: + +```rust +fn foo() -> Result<()> { + Err("foo error!".into()) +} +``` + +Introducing new error chains, with an `ErrorKind`: + +```rust +fn foo() -> Result<()> { + Err(ErrorKind::FooError.into()) +} +``` + +Note that the return type is is the typedef `Result`, which is defined +by the macro as `pub type Result = ::std::result::Result`. Note that in both cases `.into()` is called to convert a type +into the `Error` type: both strings and `ErrorKind` have `From` +conversions to turn them into `Error`. + +When the error is emitted inside a `try!` macro or behind the `?` +operator, then the explicit conversion isn't needed, since the +behavior of `try!` will automatically convert `Err(ErrorKind)` to +`Err(Error)`. So the below is equivalent to the previous: + +```rust +fn foo() -> Result<()> { + Ok(try!(Err(ErrorKind::FooError))) +} + +fn bar() -> Result<()> { + Ok(try!(Err("bogus!"))) +} +``` + +## Foreign links + +Errors that do not conform to the same conventions as this library can +still be included in the error chain. They are considered "foreign +errors", and are declared using the `foreign_links` block of the +`error_chain!` macro. `Error`s are automatically created from foreign +errors by the `try!` macro. + +Foreign links and regular links have one crucial difference: `From` +conversions for regular links *do not introduce a new error into the +error chain*, while conversions for foreign links *always introduce a +new error into the error chain*. So for the example above all errors +deriving from the `temp::Error` type will be presented to the user as +a new `ErrorKind::Temp` variant, and the cause will be the original +`temp::Error` error. In contrast, when `rustup_utils::Error` is +converted to `Error` the two `ErrorKinds` are converted between each +other to create a new `Error` but the old error is discarded; there is +no "cause" created from the original error. + +## Backtraces + +The earliest non-foreign error to be generated creates a single +backtrace, which is passed through all `From` conversions and +`chain_err` invocations of compatible types. To read the backtrace +just call the `backtrace()` method. + +## Iteration + +The `iter` method returns an iterator over the chain of error +boxes. [See how rustup uses this during error +reporting](https://github.com/rust-lang-nursery/rustup.rs/blob/master/src/rustup-cli/common.rs#L344). + +## License + +MIT/Apache-2.0 diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..194750893 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,504 @@ +//! A library for consistent and reliable error handling +//! +//! This crate defines an opinionated strategy for error handling in Rust, +//! built on the following principles: +//! +//! * No error should ever be discarded. This library primarily +//! makes it easy to "chain" errors with the `chain_err` method. +//! * Introducing new errors is trivial. Simple errors can be introduced +//! at the error site with just a string. +//! * Handling errors is possible with pattern matching. +//! * Conversions between error types are done in an automatic and +//! consistent way - `From` conversion behavior is never specified +//! explicitly. +//! * Errors implement Send. +//! * Errors carry backtraces. +//! +//! Similar to other libraries like [error-type] and [quick-error], this +//! library defines a macro, `error_chain!` that declares the types +//! and implementation boilerplate necessary for fulfilling a +//! particular error-handling strategy. Most importantly it defines +//! a custom error type (called `Error` by convention) and the `From` +//! conversions that let the `try!` macro and `?` operator work. +//! +//! This library differs in a few ways from previous error libs: +//! +//! * Instead of defining the custom `Error` type as an enum, it is a +//! struct containing an `ErrorKind` (which defines the +//! `description` and `display` methods for the error), an opaque, +//! optional, boxed `std::error::Error + Send + 'static` object +//! (which defines the `cause`, and establishes the links in the +//! error chain), and a `Backtrace`. +//! * The macro additionally defines a trait, by convention called +//! `ChainErr`, that defines a `chain_err` method. This method +//! on all `std::error::Error + Send + 'static` types extends +//! the error chain by boxing the current error into an opaque +//! object and putting it inside a new concrete error. +//! * It provides automatic `From` conversions between other error types +//! defined by the `error_chain!` that preserve type information, +//! and facilitate seamless error composition and matching of composed +//! errors. +//! * It provides automatic `From` conversions between any other error +//! type that hides the type of the other error in the `cause` box. +//! * It collects a single backtrace at the earliest opportunity and +//! propagates it down the stack through `From` and `ChainErr` +//! conversions. +//! +//! To accomplish its goals it makes some tradeoffs: +//! +//! * The split between the `Error` and `ErrorKind` types can make it +//! slightly more cumbersome to instantiate new (unchained) errors +//! errors, requiring an `Into` or `From` conversion; as well as +//! slightly more cumbersome to match on errors with another layer +//! of types to match. +//! * Because the error type contains `std::error::Error + Send + 'static` objects, +//! it can't implement `PartialEq` for easy comparisons. +//! +//! ## Declaring error types +//! +//! Generally, you define one family of error types per crate, though +//! it's also perfectly fine to define error types on a finer-grained +//! basis, such as per module. +//! +//! Assuming you are using crate-level error types, typically you will +//! define an `errors` module and inside it call `error_chain!`: +//! +//! ```ignore +//! error_chain! { +//! // The type defined for this error. These are the conventional +//! // and recommended names, but they can be arbitrarily chosen. +//! types { +//! Error, ErrorKind, ChainErr, Result; +//! } +//! +//! // Automatic conversions between this error chain and other +//! // error chains. In this case, it will e.g. generate an +//! // `ErrorKind` variant called `Dist` which in turn contains +//! // the `rustup_dist::ErrorKind`, with conversions from +//! // `rustup_dist::Error`. +//! // +//! // This section can be empty. +//! links { +//! rustup_dist::Error, rustup_dist::ErrorKind, Dist; +//! rustup_utils::Error, rustup_utils::ErrorKind, Utils; +//! } +//! +//! // Automatic conversions between this error chain and other +//! // error types not defined by the `error_chain!`. These will be +//! // boxed as the error cause and wrapped in a new error with, +//! // in this case, the `ErrorKind::Temp` variant. +//! // +//! // This section can be empty. +//! foreign_links { +//! temp::Error, Temp, +//! "temporary file error"; +//! } +//! +//! // Define additional `ErrorKind` variants. The syntax here is +//! // the same as `quick_error!`, but the `from()` and `cause()` +//! // syntax is not supported. +//! errors { +//! InvalidToolchainName(t: String) { +//! description("invalid toolchain name") +//! display("invalid toolchain name: '{}'", t) +//! } +//! } +//! } +//! ``` +//! +//! This populates the module with a number of definitions, +//! the most important of which are the `Error` type +//! and the `ErrorKind` type. They look something like the +//! following: +//! +//! ```ignore +//! use std::error::Error as StdError; +//! use std::sync::Arc; +//! +//! #[derive(Debug)] +//! pub struct Error(pub ErrorKind, +//! pub Option>, +//! pub Arc); +//! +//! impl Error { +//! pub fn kind(&self) -> &ErrorKind { ... } +//! pub fn into_kind(self) -> ErrorKind { ... } +//! pub fn iter(&self) -> error_chain::ErrorChainIter { ... } +//! pub fn backtrace(&self) -> &error_chain::Backtrace { ... } +//! } +//! +//! impl StdError for Error { ... } +//! impl Display for Error { ... } +//! +//! #[derive(Debug)] +//! pub enum ErrorKind { +//! Msg(String), +//! Dist(rustup_dist::ErrorKind), +//! Utils(rustup_utils::ErrorKind), +//! Temp, +//! InvalidToolchainName(String), +//! } +//! ``` +//! +//! This is the basic error structure. You can see that `ErrorKind` +//! has been populated in a variety of ways. All `ErrorKind`s get a +//! `Msg` variant for basic errors. When strings are converted to +//! `ErrorKind`s they become `ErrorKind::Msg`. The "links" defined in +//! the macro are expanded to `Dist` and `Utils` variants, and the +//! "foreign links" to the `Temp` variant. +//! +//! Both types come with a variety of `From` conversions as well: +//! `Error` can be created from `ErrorKind`, `&str` and `String`, +//! and the "link" and "foreign_link" error types. `ErrorKind` +//! can be created from the corresponding `ErrorKind`s of the link +//! types, as well as from `&str` and `String`. +//! +//! `into()` and `From::from` are used heavily to massage types into +//! the right shape. Which one to use in any specific case depends on +//! the influence of type inference, but there are some patterns that +//! arise frequently. +//! +//! ## Returning new errors +//! +//! Introducing new error chains, with a string message: +//! +//! ```ignore +//! fn foo() -> Result<()> { +//! Err("foo error!".into()) +//! } +//! ``` +//! +//! Introducing new error chains, with an `ErrorKind`: +//! +//! ```ignore +//! fn foo() -> Result<()> { +//! Err(ErrorKind::FooError.into()) +//! } +//! ``` +//! +//! Note that the return type is the typedef `Result`, which is +//! defined by the macro as `pub type Result = +//! ::std::result::Result`. Note that in both cases +//! `.into()` is called to convert a type into the `Error` type; both +//! strings and `ErrorKind` have `From` conversions to turn them into +//! `Error`. +//! +//! When the error is emitted inside a `try!` macro or behind the +//! `?` operator, the explicit conversion isn't needed; `try!` will +//! automatically convert `Err(ErrorKind)` to `Err(Error)`. So the +//! below is equivalent to the previous: +//! +//! ```ignore +//! fn foo() -> Result<()> { +//! Ok(try!(Err(ErrorKind::FooError))) +//! } +//! +//! fn bar() -> Result<()> { +//! Ok(try!(Err("bogus!"))) +//! } +//! ``` +//! +//! ## Chaining errors +//! +//! To extend the error chain: +//! +//! ```ignore +//! use errors::ChainErr; +//! try!(do_something().chain_err(|| "something went wrong")); +//! ``` +//! +//! `chain_err` can be called on any `Result` type where the contained +//! error type implements `std::error::Error + Send + 'static`. If +//! the `Result` is an `Err` then `chain_err` evaluates the closure, +//! which returns *some type that can be converted to `ErrorKind`*, +//! boxes the original error to store as the cause, then returns a new +//! error containing the original error. +//! +//! ## Foreign links +//! +//! Errors that do not conform to the same conventions as this library +//! can still be included in the error chain. They are considered "foreign +//! errors", and are declared using the `foreign_links` block of the +//! `error_chain!` macro. `Error`s are automatically created from +//! foreign errors by the `try!` macro. +//! +//! Foreign links and regular links have one crucial difference: +//! `From` conversions for regular links *do not introduce a new error +//! into the error chain*, while conversions for foreign links *always +//! introduce a new error into the error chain*. So for the example +//! above all errors deriving from the `temp::Error` type will be +//! presented to the user as a new `ErrorKind::Temp` variant, and the +//! cause will be the original `temp::Error` error. In contrast, when +//! `rustup_utils::Error` is converted to `Error` the two `ErrorKinds` +//! are converted between each other to create a new `Error` but the +//! old error is discarded; there is no "cause" created from the +//! original error. +//! +//! ## Backtraces +//! +//! The earliest non-foreign error to be generated creates a single +//! backtrace, which is passed through all `From` conversions and +//! `chain_err` invocations of compatible types. To read the backtrace +//! just call the `backtrace()` method. +//! +//! ## Iteration +//! +//! The `iter` method returns an iterator over the chain of error boxes. +//! +//! [error-type]: https://github.com/DanielKeep/rust-error-type +//! [quick-error]: https://github.com/tailhook/quick-error + +extern crate backtrace; + +pub use backtrace::Backtrace; + +mod quick_error; + +#[macro_export] +macro_rules! error_chain { + ( + types { + $error_name:ident, $error_kind_name:ident, + $chain_error_name:ident, $result_name:ident; + } + + links { + $( $link_error_path:path, $link_kind_path:path, $link_variant:ident; ) * + } + + foreign_links { + $( $foreign_link_error_path:path, $foreign_link_variant:ident, + $foreign_link_desc:expr; ) * + } + + errors { + $( $error_chunks:tt ) * + } + + ) => { + + + // The Error type + // -------------- + + // This has a simple structure to support pattern matching + // during error handling. The second field is internal state + // that is mostly irrelevant for error handling purposes. + #[derive(Debug)] + pub struct $error_name(pub $error_kind_name, + pub (Option>, + ::std::sync::Arc<$crate::Backtrace>)); + + #[allow(unused)] + impl $error_name { + pub fn kind(&self) -> &$error_kind_name { + &self.0 + } + + pub fn into_kind(self) -> $error_kind_name { + self.0 + } + + pub fn iter(&self) -> $crate::ErrorChainIter { + $crate::ErrorChainIter(Some(self)) + } + + pub fn backtrace(&self) -> &$crate::Backtrace { + &(self.1).1 + } + } + + impl ::std::error::Error for $error_name { + fn description(&self) -> &str { self.0.description() } + fn cause(&self) -> Option<&::std::error::Error> { + match (self.1).0 { + Some(ref c) => Some(&**c), + None => None + } + } + } + + impl ::std::fmt::Display for $error_name { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + ::std::fmt::Display::fmt(&self.0, f) + } + } + + $( + impl From<$link_error_path> for $error_name { + fn from(e: $link_error_path) -> Self { + $error_name($error_kind_name::$link_variant(e.0), e.1) + } + } + ) * + + $( + impl From<$foreign_link_error_path> for $error_name { + fn from(e: $foreign_link_error_path) -> Self { + $error_name( + $error_kind_name::$foreign_link_variant, + (Some(Box::new(e)), + ::std::sync::Arc::new($crate::Backtrace::new()))) + } + } + ) * + + impl From<$error_kind_name> for $error_name { + fn from(e: $error_kind_name) -> Self { + $error_name(e, + (None, ::std::sync::Arc::new($crate::Backtrace::new()))) + } + } + + impl<'a> From<&'a str> for $error_name { + fn from(s: &'a str) -> Self { + $error_name(s.into(), + (None, ::std::sync::Arc::new($crate::Backtrace::new()))) + } + } + + impl From for $error_name { + fn from(s: String) -> Self { + $error_name(s.into(), + (None, ::std::sync::Arc::new($crate::Backtrace::new()))) + } + } + + + // The ErrorKind type + // -------------- + + quick_error! { + #[derive(Debug)] + pub enum $error_kind_name { + + Msg(s: String) { + description(&s) + display("{}", s) + } + + $( + $link_variant(e: $link_kind_path) { + description(e.description()) + display("{}", e) + } + ) * + + $( + $foreign_link_variant { + description(&$foreign_link_desc) + } + ) * + + $($error_chunks)* + } + } + + $( + impl From<$link_kind_path> for $error_kind_name { + fn from(e: $link_kind_path) -> Self { + $error_kind_name::$link_variant(e) + } + } + ) * + + impl<'a> From<&'a str> for $error_kind_name { + fn from(s: &'a str) -> Self { + $error_kind_name::Msg(s.to_string()) + } + } + + impl From for $error_kind_name { + fn from(s: String) -> Self { + $error_kind_name::Msg(s) + } + } + + + // The ChainErr trait + // ------------------ + + pub trait $chain_error_name { + fn chain_err(self, callback: F) -> ::std::result::Result + where F: FnOnce() -> EK, + EK: Into<$error_kind_name>; + } + + impl $chain_error_name for ::std::result::Result + where E: ::std::error::Error + Send + 'static + { + fn chain_err(self, callback: F) -> ::std::result::Result + where F: FnOnce() -> EK, + EK: Into<$error_kind_name> + { + self.map_err(move |e| { + let e = Box::new(e) as Box<::std::error::Error + Send + 'static>; + let (e, backtrace) = backtrace_from_box(e); + let backtrace = backtrace.unwrap_or_else( + || ::std::sync::Arc::new($crate::Backtrace::new())); + + $error_name(callback().into(), (Some(e), backtrace)) + }) + } + } + + // Use downcasts to extract the backtrace from types we know, + // to avoid generating a new one. It would be better to not + // define this in the macro, but types need some additional + // machinery to make it work. + fn backtrace_from_box(mut e: Box<::std::error::Error + Send + 'static>) + -> (Box<::std::error::Error + Send + 'static>, + Option<::std::sync::Arc<$crate::Backtrace>>) { + let mut backtrace = None; + + e = match e.downcast::<$error_name>() { + Err(e) => e, + Ok(e) => { + backtrace = Some((e.1).1.clone()); + e as Box<::std::error::Error + Send + 'static> + } + }; + + $( + + e = match e.downcast::<$link_error_path>() { + Err(e) => e, + Ok(e) => { + backtrace = Some((e.1).1.clone()); + e as Box<::std::error::Error + Send + 'static> + } + }; + + ) * + + (e, backtrace) + } + + // The Result type + // --------------- + + pub type $result_name = ::std::result::Result; + }; +} + + +use std::error::Error as StdError; +use std::iter::Iterator; + +pub struct ErrorChainIter<'a>(pub Option<&'a StdError>); + +impl<'a> Iterator for ErrorChainIter<'a> { + + type Item = &'a StdError; + + fn next<'b>(&'b mut self) -> Option<&'a StdError> { + match self.0.take() { + Some(e) => { + self.0 = e.cause(); + Some(e) + } + None => None + } + } +} + diff --git a/src/quick_error.rs b/src/quick_error.rs new file mode 100644 index 000000000..813628abc --- /dev/null +++ b/src/quick_error.rs @@ -0,0 +1,521 @@ +// From https://github.com/tailhook/quick-error + +#[macro_export] +macro_rules! quick_error { + ( $(#[$meta:meta])* + pub enum $name:ident { $($chunks:tt)* } + ) => { + quick_error!(SORT [pub enum $name $(#[$meta])* ] + items [] buf [] + queue [ $($chunks)* ]); + }; + ( $(#[$meta:meta])* + enum $name:ident { $($chunks:tt)* } + ) => { + quick_error!(SORT [enum $name $(#[$meta])* ] + items [] buf [] + queue [ $($chunks)* ]); + }; + // Queue is empty, can do the work + (SORT [enum $name:ident $( #[$meta:meta] )*] + items [$($( #[$imeta:meta] )* + => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] + {$( $ifuncs:tt )*} )* ] + buf [ ] + queue [ ] + ) => { + quick_error!(ENUM_DEFINITION [enum $name $( #[$meta] )*] + body [] + queue [$($( #[$imeta] )* + => $iitem: $imode [$( $ivar: $ityp ),*] )*] + ); + quick_error!(IMPLEMENTATIONS $name {$( + $iitem: $imode [$( $ivar: $ityp ),*] {$( $ifuncs )*} + )*}); + $( + quick_error!(ERROR_CHECK $imode $($ifuncs)*); + )* + }; + (SORT [pub enum $name:ident $( #[$meta:meta] )*] + items [$($( #[$imeta:meta] )* + => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] + {$( $ifuncs:tt )*} )* ] + buf [ ] + queue [ ] + ) => { + quick_error!(ENUM_DEFINITION [pub enum $name $( #[$meta] )*] + body [] + queue [$($( #[$imeta] )* + => $iitem: $imode [$( $ivar: $ityp ),*] )*] + ); + quick_error!(IMPLEMENTATIONS $name {$( + $iitem: $imode [$( $ivar: $ityp ),*] {$( $ifuncs )*} + )*}); + $( + quick_error!(ERROR_CHECK $imode $($ifuncs)*); + )* + }; + // Add meta to buffer + (SORT [$( $def:tt )*] + items [$($( #[$imeta:meta] )* + => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] + {$( $ifuncs:tt )*} )* ] + buf [$( #[$bmeta:meta] )*] + queue [ #[$qmeta:meta] $( $tail:tt )*] + ) => { + quick_error!(SORT [$( $def )*] + items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*] + buf [$( #[$bmeta] )* #[$qmeta] ] + queue [$( $tail )*]); + }; + // Add ident to buffer + (SORT [$( $def:tt )*] + items [$($( #[$imeta:meta] )* + => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] + {$( $ifuncs:tt )*} )* ] + buf [$( #[$bmeta:meta] )*] + queue [ $qitem:ident $( $tail:tt )*] + ) => { + quick_error!(SORT [$( $def )*] + items [$( $(#[$imeta])* + => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*] + buf [$(#[$bmeta])* => $qitem : UNIT [ ] ] + queue [$( $tail )*]); + }; + // Flush buffer on meta after ident + (SORT [$( $def:tt )*] + items [$($( #[$imeta:meta] )* + => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] + {$( $ifuncs:tt )*} )* ] + buf [$( #[$bmeta:meta] )* + => $bitem:ident: $bmode:tt [$( $bvar:ident: $btyp:ty ),*] ] + queue [ #[$qmeta:meta] $( $tail:tt )*] + ) => { + quick_error!(SORT [$( $def )*] + enum [$( $(#[$emeta])* => $eitem $(( $($etyp),* ))* )* + $(#[$bmeta])* => $bitem: $bmode $(( $($btyp),* ))*] + items [$($( #[$imeta:meta] )* + => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )* + $bitem: $bmode [$( $bvar:$btyp ),*] {} ] + buf [ #[$qmeta] ] + queue [$( $tail )*]); + }; + // Add tuple enum-variant + (SORT [$( $def:tt )*] + items [$($( #[$imeta:meta] )* + => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] + {$( $ifuncs:tt )*} )* ] + buf [$( #[$bmeta:meta] )* => $bitem:ident: UNIT [ ] ] + queue [($( $qvar:ident: $qtyp:ty ),+) $( $tail:tt )*] + ) => { + quick_error!(SORT [$( $def )*] + items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*] + buf [$( #[$bmeta] )* => $bitem: TUPLE [$( $qvar:$qtyp ),*] ] + queue [$( $tail )*] + ); + }; + // Add struct enum-variant - e.g. { descr: &'static str } + (SORT [$( $def:tt )*] + items [$($( #[$imeta:meta] )* + => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] + {$( $ifuncs:tt )*} )* ] + buf [$( #[$bmeta:meta] )* => $bitem:ident: UNIT [ ] ] + queue [{ $( $qvar:ident: $qtyp:ty ),+} $( $tail:tt )*] + ) => { + quick_error!(SORT [$( $def )*] + items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*] + buf [$( #[$bmeta] )* => $bitem: STRUCT [$( $qvar:$qtyp ),*] ] + queue [$( $tail )*]); + }; + // Add struct enum-variant, with excess comma - e.g. { descr: &'static str, } + (SORT [$( $def:tt )*] + items [$($( #[$imeta:meta] )* + => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] + {$( $ifuncs:tt )*} )* ] + buf [$( #[$bmeta:meta] )* => $bitem:ident: UNIT [ ] ] + queue [{$( $qvar:ident: $qtyp:ty ),+ ,} $( $tail:tt )*] + ) => { + quick_error!(SORT [$( $def )*] + items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )*] + buf [$( #[$bmeta] )* => $bitem: STRUCT [$( $qvar:$qtyp ),*] ] + queue [$( $tail )*]); + }; + // Add braces and flush always on braces + (SORT [$( $def:tt )*] + items [$($( #[$imeta:meta] )* + => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] + {$( $ifuncs:tt )*} )* ] + buf [$( #[$bmeta:meta] )* + => $bitem:ident: $bmode:tt [$( $bvar:ident: $btyp:ty ),*] ] + queue [ {$( $qfuncs:tt )*} $( $tail:tt )*] + ) => { + quick_error!(SORT [$( $def )*] + items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )* + $(#[$bmeta])* => $bitem: $bmode [$( $bvar:$btyp ),*] {$( $qfuncs )*} ] + buf [ ] + queue [$( $tail )*]); + }; + // Flush buffer on double ident + (SORT [$( $def:tt )*] + items [$($( #[$imeta:meta] )* + => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] + {$( $ifuncs:tt )*} )* ] + buf [$( #[$bmeta:meta] )* + => $bitem:ident: $bmode:tt [$( $bvar:ident: $btyp:ty ),*] ] + queue [ $qitem:ident $( $tail:tt )*] + ) => { + quick_error!(SORT [$( $def )*] + items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )* + $(#[$bmeta])* => $bitem: $bmode [$( $bvar:$btyp ),*] {} ] + buf [ => $qitem : UNIT [ ] ] + queue [$( $tail )*]); + }; + // Flush buffer on end + (SORT [$( $def:tt )*] + items [$($( #[$imeta:meta] )* + => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] + {$( $ifuncs:tt )*} )* ] + buf [$( #[$bmeta:meta] )* + => $bitem:ident: $bmode:tt [$( $bvar:ident: $btyp:ty ),*] ] + queue [ ] + ) => { + quick_error!(SORT [$( $def )*] + items [$( $(#[$imeta])* => $iitem: $imode [$( $ivar:$ityp ),*] {$( $ifuncs )*} )* + $(#[$bmeta])* => $bitem: $bmode [$( $bvar:$btyp ),*] {} ] + buf [ ] + queue [ ]); + }; + // Public enum (Queue Empty) + (ENUM_DEFINITION [pub enum $name:ident $( #[$meta:meta] )*] + body [$($( #[$imeta:meta] )* + => $iitem:ident ($(($( $ttyp:ty ),+))*) {$({$( $svar:ident: $styp:ty ),*})*} )* ] + queue [ ] + ) => { + $(#[$meta])* + pub enum $name { + $( + $(#[$imeta])* + $iitem $(($( $ttyp ),*))* $({$( $svar: $styp ),*})*, + )* + } + }; + // Private enum (Queue Empty) + (ENUM_DEFINITION [enum $name:ident $( #[$meta:meta] )*] + body [$($( #[$imeta:meta] )* + => $iitem:ident ($(($( $ttyp:ty ),+))*) {$({$( $svar:ident: $styp:ty ),*})*} )* ] + queue [ ] + ) => { + $(#[$meta])* + enum $name { + $( + $(#[$imeta])* + $iitem $(($( $ttyp ),*))* $({$( $svar: $styp ),*})*, + )* + } + }; + // Unit variant + (ENUM_DEFINITION [$( $def:tt )*] + body [$($( #[$imeta:meta] )* + => $iitem:ident ($(($( $ttyp:ty ),+))*) {$({$( $svar:ident: $styp:ty ),*})*} )* ] + queue [$( #[$qmeta:meta] )* + => $qitem:ident: UNIT [ ] $( $queue:tt )*] + ) => { + quick_error!(ENUM_DEFINITION [ $($def)* ] + body [$($( #[$imeta] )* => $iitem ($(($( $ttyp ),+))*) {$({$( $svar: $styp ),*})*} )* + $( #[$qmeta] )* => $qitem () {} ] + queue [ $($queue)* ] + ); + }; + // Tuple variant + (ENUM_DEFINITION [$( $def:tt )*] + body [$($( #[$imeta:meta] )* + => $iitem:ident ($(($( $ttyp:ty ),+))*) {$({$( $svar:ident: $styp:ty ),*})*} )* ] + queue [$( #[$qmeta:meta] )* + => $qitem:ident: TUPLE [$( $qvar:ident: $qtyp:ty ),+] $( $queue:tt )*] + ) => { + quick_error!(ENUM_DEFINITION [ $($def)* ] + body [$($( #[$imeta] )* => $iitem ($(($( $ttyp ),+))*) {$({$( $svar: $styp ),*})*} )* + $( #[$qmeta] )* => $qitem (($( $qtyp ),*)) {} ] + queue [ $($queue)* ] + ); + }; + // Struct variant + (ENUM_DEFINITION [$( $def:tt )*] + body [$($( #[$imeta:meta] )* + => $iitem:ident ($(($( $ttyp:ty ),+))*) {$({$( $svar:ident: $styp:ty ),*})*} )* ] + queue [$( #[$qmeta:meta] )* + => $qitem:ident: STRUCT [$( $qvar:ident: $qtyp:ty ),*] $( $queue:tt )*] + ) => { + quick_error!(ENUM_DEFINITION [ $($def)* ] + body [$($( #[$imeta] )* => $iitem ($(($( $ttyp ),+))*) {$({$( $svar: $styp ),*})*} )* + $( #[$qmeta] )* => $qitem () {{$( $qvar: $qtyp ),*}} ] + queue [ $($queue)* ] + ); + }; + (IMPLEMENTATIONS + $name:ident {$( + $item:ident: $imode:tt [$( $var:ident: $typ:ty ),*] {$( $funcs:tt )*} + )*} + ) => { + #[allow(unused)] + impl ::std::fmt::Display for $name { + fn fmt(&self, fmt: &mut ::std::fmt::Formatter) + -> ::std::fmt::Result + { + match *self { + $( + quick_error!(ITEM_PATTERN + $name $item: $imode [$( ref $var ),*] + ) => { + let display_fn = quick_error!(FIND_DISPLAY_IMPL + $name $item: $imode + {$( $funcs )*}); + + display_fn(self, fmt) + } + )* + } + } + } + /*#[allow(unused)] + impl ::std::error::Error for $name { + fn description(&self) -> &str { + match *self { + $( + quick_error!(ITEM_PATTERN + $name $item: $imode [$( ref $var ),*] + ) => { + quick_error!(FIND_DESCRIPTION_IMPL + $item: $imode self fmt [$( $var ),*] + {$( $funcs )*}) + } + )* + } + } + fn cause(&self) -> Option<&::std::error::Error> { + match *self { + $( + quick_error!(ITEM_PATTERN + $name $item: $imode [$( ref $var ),*] + ) => { + quick_error!(FIND_CAUSE_IMPL + $item: $imode [$( $var ),*] + {$( $funcs )*}) + } + )* + } + } + }*/ + #[allow(unused)] + impl $name { + pub fn description(&self) -> &str { + match *self { + $( + quick_error!(ITEM_PATTERN + $name $item: $imode [$( ref $var ),*] + ) => { + quick_error!(FIND_DESCRIPTION_IMPL + $item: $imode self fmt [$( $var ),*] + {$( $funcs )*}) + } + )* + } + } + } + $( + quick_error!(FIND_FROM_IMPL + $name $item: $imode [$( $var:$typ ),*] + {$( $funcs )*}); + )* + }; + (FIND_DISPLAY_IMPL $name:ident $item:ident: $imode:tt + { display($self_:tt) -> ($( $exprs:tt )*) $( $tail:tt )*} + ) => { + |quick_error!(IDENT $self_): &$name, f: &mut ::std::fmt::Formatter| { write!(f, $( $exprs )*) } + }; + (FIND_DISPLAY_IMPL $name:ident $item:ident: $imode:tt + { display($pattern:expr) $( $tail:tt )*} + ) => { + |_, f: &mut ::std::fmt::Formatter| { write!(f, $pattern) } + }; + (FIND_DISPLAY_IMPL $name:ident $item:ident: $imode:tt + { display($pattern:expr, $( $exprs:tt )*) $( $tail:tt )*} + ) => { + |_, f: &mut ::std::fmt::Formatter| { write!(f, $pattern, $( $exprs )*) } + }; + (FIND_DISPLAY_IMPL $name:ident $item:ident: $imode:tt + { $t:tt $( $tail:tt )*} + ) => { + quick_error!(FIND_DISPLAY_IMPL + $name $item: $imode + {$( $tail )*}) + }; + (FIND_DISPLAY_IMPL $name:ident $item:ident: $imode:tt + { } + ) => { + |self_: &$name, f: &mut ::std::fmt::Formatter| { + write!(f, "{}", self_.description()) + } + }; + (FIND_DESCRIPTION_IMPL $item:ident: $imode:tt $me:ident $fmt:ident + [$( $var:ident ),*] + { description($expr:expr) $( $tail:tt )*} + ) => { + $expr + }; + (FIND_DESCRIPTION_IMPL $item:ident: $imode:tt $me:ident $fmt:ident + [$( $var:ident ),*] + { $t:tt $( $tail:tt )*} + ) => { + quick_error!(FIND_DESCRIPTION_IMPL + $item: $imode $me $fmt [$( $var ),*] + {$( $tail )*}) + }; + (FIND_DESCRIPTION_IMPL $item:ident: $imode:tt $me:ident $fmt:ident + [$( $var:ident ),*] + { } + ) => { + stringify!($item) + }; + (FIND_CAUSE_IMPL $item:ident: $imode:tt + [$( $var:ident ),*] + { cause($expr:expr) $( $tail:tt )*} + ) => { + Some($expr) + }; + (FIND_CAUSE_IMPL $item:ident: $imode:tt + [$( $var:ident ),*] + { $t:tt $( $tail:tt )*} + ) => { + quick_error!(FIND_CAUSE_IMPL + $item: $imode [$( $var ),*] + { $($tail)* }) + }; + (FIND_CAUSE_IMPL $item:ident: $imode:tt + [$( $var:ident ),*] + { } + ) => { + None + }; + (FIND_FROM_IMPL $name:ident $item:ident: $imode:tt + [$( $var:ident: $typ:ty ),*] + { from() $( $tail:tt )*} + ) => { + $( + impl From<$typ> for $name { + fn from($var: $typ) -> $name { + $name::$item($var) + } + } + )* + quick_error!(FIND_FROM_IMPL + $name $item: $imode [$( $var:$typ ),*] + {$( $tail )*}); + }; + (FIND_FROM_IMPL $name:ident $item:ident: UNIT + [ ] + { from($ftyp:ty) $( $tail:tt )*} + ) => { + impl From<$ftyp> for $name { + fn from(_discarded_error: $ftyp) -> $name { + $name::$item + } + } + quick_error!(FIND_FROM_IMPL + $name $item: UNIT [ ] + {$( $tail )*}); + }; + (FIND_FROM_IMPL $name:ident $item:ident: TUPLE + [$( $var:ident: $typ:ty ),*] + { from($fvar:ident: $ftyp:ty) -> ($( $texpr:expr ),*) $( $tail:tt )*} + ) => { + impl From<$ftyp> for $name { + fn from($fvar: $ftyp) -> $name { + $name::$item($( $texpr ),*) + } + } + quick_error!(FIND_FROM_IMPL + $name $item: TUPLE [$( $var:$typ ),*] + { $($tail)* }); + }; + (FIND_FROM_IMPL $name:ident $item:ident: STRUCT + [$( $var:ident: $typ:ty ),*] + { from($fvar:ident: $ftyp:ty) -> {$( $tvar:ident: $texpr:expr ),*} $( $tail:tt )*} + ) => { + impl From<$ftyp> for $name { + fn from($fvar: $ftyp) -> $name { + $name::$item { + $( $tvar: $texpr ),* + } + } + } + quick_error!(FIND_FROM_IMPL + $name $item: STRUCT [$( $var:$typ ),*] + { $($tail)* }); + }; + (FIND_FROM_IMPL $name:ident $item:ident: $imode:tt + [$( $var:ident: $typ:ty ),*] + { $t:tt $( $tail:tt )*} + ) => { + quick_error!(FIND_FROM_IMPL + $name $item: $imode [$( $var:$typ ),*] + {$( $tail )*} + ); + }; + (FIND_FROM_IMPL $name:ident $item:ident: $imode:tt + [$( $var:ident: $typ:ty ),*] + { } + ) => { + }; + (ITEM_BODY $(#[$imeta:meta])* $item:ident: UNIT + ) => { }; + (ITEM_BODY $(#[$imeta:meta])* $item:ident: TUPLE + [$( $typ:ty ),*] + ) => { + ($( $typ ),*) + }; + (ITEM_BODY $(#[$imeta:meta])* $item:ident: STRUCT + [$( $var:ident: $typ:ty ),*] + ) => { + {$( $var:$typ ),*} + }; + (ITEM_PATTERN $name:ident $item:ident: UNIT [] + ) => { + $name::$item + }; + (ITEM_PATTERN $name:ident $item:ident: TUPLE + [$( ref $var:ident ),*] + ) => { + $name::$item ($( ref $var ),*) + }; + (ITEM_PATTERN $name:ident $item:ident: STRUCT + [$( ref $var:ident ),*] + ) => { + $name::$item {$( ref $var ),*} + }; + // This one should match all allowed sequences in "funcs" but not match + // anything else. + // This is to contrast FIND_* clauses which just find stuff they need and + // skip everything else completely + (ERROR_CHECK $imode:tt display($self_:tt) -> ($( $exprs:tt )*) $( $tail:tt )*) + => { quick_error!(ERROR_CHECK $imode $($tail)*); }; + (ERROR_CHECK $imode:tt display($pattern: expr) $( $tail:tt )*) + => { quick_error!(ERROR_CHECK $imode $($tail)*); }; + (ERROR_CHECK $imode:tt display($pattern: expr, $( $exprs:tt )*) $( $tail:tt )*) + => { quick_error!(ERROR_CHECK $imode $($tail)*); }; + (ERROR_CHECK $imode:tt description($expr:expr) $( $tail:tt )*) + => { quick_error!(ERROR_CHECK $imode $($tail)*); }; + (ERROR_CHECK $imode:tt cause($expr:expr) $($tail:tt)*) + => { quick_error!(ERROR_CHECK $imode $($tail)*); }; + (ERROR_CHECK $imode:tt from() $($tail:tt)*) + => { quick_error!(ERROR_CHECK $imode $($tail)*); }; + (ERROR_CHECK $imode:tt from($ftyp:ty) $($tail:tt)*) + => { quick_error!(ERROR_CHECK $imode $($tail)*); }; + (ERROR_CHECK TUPLE from($fvar:ident: $ftyp:ty) -> ($( $e:expr ),*) $( $tail:tt )*) + => { quick_error!(ERROR_CHECK TUPLE $($tail)*); }; + (ERROR_CHECK STRUCT from($fvar:ident: $ftyp:ty) -> {$( $v:ident: $e:expr ),*} $( $tail:tt )*) + => { quick_error!(ERROR_CHECK STRUCT $($tail)*); }; + (ERROR_CHECK $imode:tt ) => {}; + // Utility functions + (IDENT $ident:ident) => { $ident } +}