From 82a27f397be1c045454c01aef194e9a75a3f3335 Mon Sep 17 00:00:00 2001 From: Yamakaky Date: Tue, 22 Nov 2016 18:57:04 -0500 Subject: [PATCH] Add `boxed-error` feature. Fixes #44. --- .travis.yml | 3 +- CHANGELOG.md | 1 + Cargo.toml | 5 +- README.md | 7 +++ src/error_chain.rs | 123 +++++++++++++++++++++++++++++++++------------ src/lib.rs | 10 ++-- 6 files changed, 108 insertions(+), 41 deletions(-) diff --git a/.travis.yml b/.travis.yml index 21c9b8c59..215ead0ca 100644 --- a/.travis.yml +++ b/.travis.yml @@ -30,5 +30,6 @@ env: global: - secure: ncxJbvJM1vCZfcEftjsFKJMxxhKLgWKaR8Go9AMo0VB5fB2XVW/6NYO5bQEEYpOf1Nc/+2FbI2+Dkz0S/mJpUcNSfBgablCHgwU2sHse7KsoaqfHj2mf1E3exjzSHoP96hPGicC5zAjSXFjCgJPOUSGqqRaJ7z5AsJLhJT6LuK7QpvwPBZzklUN8T+n1sVmws8TNmRIbaniq/q6wYHANHcy6Dl59dx4sKwniUGiZdUhCiddVpoxbECSxc0A8mN2pk7/aW+WGxK3goBs5ZF7+JXF318F62pDcXQmR5CX6WdpenIcJ25g1Vg1WhQ4Ifpe17CN0bfxV8ShuzrQUThCDMffZCo9XySBtODdEowwK1UIpjnFLfIxjOs45Cd8o3tM2j0CfvtnjOz6BCdUU0qiwNPPNx0wFkx3ZiOfSh+FhBhvyPM12HN2tdN0esgVBItFmEci+sSIIXqjVL6DNiu5zTjbu0bs6COwlUWdmL6vmsZtq5tl7Cno9+C3szxRVAkShGydd04l9NYjqNEzTa1EPG50OsnVRKGdRiFzSxhc3BWExNKvcQ4v867t6/PpPkW6s4oXmYI3+De+8O7ExWc6a4alcrDXKlMs5fCb5Pcd4Ju9kowcjkoJo5yf2wW3Ox5R8SJpaEEpvyhx5O/qtIxjhHNzeo8Wsr/6gdNDv20r91TI= matrix: - - FEATURES=--features=backtrace - FEATURES=--no-default-features + - FEATURES=--no-default-features --features=boxed-error + - FEATURES=--all-features diff --git a/CHANGELOG.md b/CHANGELOG.md index dbfb7636e..563ecd1f4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ - New `Variant(Error) #[attrs]` for `links` and `foreign_links`. - Hide implementation details from the doc. +- Add `boxed-error` feature (#69). # 0.6.1 diff --git a/Cargo.toml b/Cargo.toml index 1d94ad5cb..bbad6cd40 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,8 +15,9 @@ repository = "https://github.com/brson/error-chain" license = "MIT/Apache-2.0" [features] -default = ["backtrace", "example_generated"] -example_generated = [] +default = ["backtrace", "example-generated"] +example-generated = [] +boxed-error = [] [dependencies] backtrace = { version = "0.3", optional = true } diff --git a/README.md b/README.md index 839279183..b6ec1f800 100644 --- a/README.md +++ b/README.md @@ -21,6 +21,13 @@ provides a few unique features: See the [quickstart example](examples/quickstart.rs). +## Features + +- `backtrace`: generates backtraces on error creation. +- `boxed-error`: the error is boxed (see #44). +- `example-generated`: generates the example in the documentation (should be + disabled). + ## License MIT/Apache-2.0 diff --git a/src/error_chain.rs b/src/error_chain.rs index 5555904db..58b41b125 100644 --- a/src/error_chain.rs +++ b/src/error_chain.rs @@ -50,15 +50,12 @@ macro_rules! error_chain_processed { } ) => { - /// The Error type. - /// - /// This struct is made of three things: - /// - /// - an `ErrorKind` which is used to determine the type of the error. - /// - a backtrace, generated when the error is created. - /// - an error chain, used for the implementation of `Error::cause()`. + define_error!($error_name, Inner); + + /// Separated for the `boxed-error` feature. #[derive(Debug)] - pub struct $error_name { + #[doc(hidden)] + pub struct Inner { // The members must be `pub` for `links`. /// The kind of the error. #[doc(hidden)] @@ -68,16 +65,17 @@ macro_rules! error_chain_processed { pub state: $crate::State, } + impl Inner { + /// Used in From. + pub fn into_raw(self) -> ($error_kind_name, $crate::State) { + (self.kind, self.state) + } + } + impl $crate::ChainedError for $error_name { type ErrorKind = $error_kind_name; - fn new(kind: $error_kind_name, state: $crate::State) -> $error_name { - $error_name { - kind: kind, - state: state, - } - } - + impl_chained_error_new!($error_name, $error_kind_name); impl_extract_backtrace!($error_name $error_kind_name $([$link_error_path, $(#[$meta_links])*])*); @@ -87,15 +85,13 @@ macro_rules! error_chain_processed { impl $error_name { /// Constructs an error from a kind, and generates a backtrace. pub fn from_kind(kind: $error_kind_name) -> $error_name { - $error_name { - kind: kind, - state: $crate::State::default(), - } + use $crate::ChainedError; + Self::new(kind, $crate::State::default()) } /// Returns the kind of the error. pub fn kind(&self) -> &$error_kind_name { - &self.kind + &self.0.kind } /// Iterates over the error chain. @@ -105,20 +101,20 @@ macro_rules! error_chain_processed { /// Returns the backtrace associated with this error. pub fn backtrace(&self) -> Option<&$crate::Backtrace> { - self.state.backtrace() + self.0.state.backtrace() } } impl ::std::error::Error for $error_name { fn description(&self) -> &str { - self.kind.description() + self.0.kind.description() } fn cause(&self) -> Option<&::std::error::Error> { - match self.state.next_error { + match self.0.state.next_error { Some(ref c) => Some(&**c), None => { - match self.kind { + match self.0.kind { $( $(#[$meta_foreign_links])* $error_kind_name::$foreign_link_variant(ref foreign_err) => { @@ -134,7 +130,7 @@ macro_rules! error_chain_processed { impl ::std::fmt::Display for $error_name { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - ::std::fmt::Display::fmt(&self.kind, f) + ::std::fmt::Display::fmt(&self.0.kind, f) } } @@ -142,10 +138,9 @@ macro_rules! error_chain_processed { $(#[$meta_links])* impl From<$link_error_path> for $error_name { fn from(e: $link_error_path) -> Self { - $error_name { - kind: $error_kind_name::$link_variant(e.kind), - state: e.state, - } + use $crate::ChainedError; + let (kind, state) = e.0.into_raw(); + Self::new($error_kind_name::$link_variant(kind), state) } } ) * @@ -183,7 +178,7 @@ macro_rules! error_chain_processed { type Target = $error_kind_name; fn deref(&self) -> &Self::Target { - &self.kind + &self.0.kind } } @@ -245,7 +240,7 @@ macro_rules! error_chain_processed { impl From<$error_name> for $error_kind_name { fn from(e: $error_name) -> Self { - e.kind + e.0.kind } } }; @@ -332,13 +327,13 @@ macro_rules! impl_extract_backtrace { fn extract_backtrace(e: &(::std::error::Error + Send + 'static)) -> Option>> { if let Some(e) = e.downcast_ref::<$error_name>() { - return Some(e.state.backtrace.clone()); + return Some(e.0.state.backtrace.clone()); } $( $( #[$meta_links] )* { if let Some(e) = e.downcast_ref::<$link_error_path>() { - return Some(e.state.backtrace.clone()); + return Some(e.0.state.backtrace.clone()); } } ) * @@ -360,3 +355,65 @@ macro_rules! impl_extract_backtrace { $error_kind_name: ident $([$link_error_path: path, $(#[$meta_links: meta])*])*) => {} } + +#[macro_export] +#[doc(hidden)] +#[cfg(feature = "boxed-error")] +macro_rules! impl_chained_error_new { + ($error_name: ident, $error_kind_name: ident) => { + fn new(kind: $error_kind_name, state: $crate::State) -> $error_name { + $error_name(Box::new(Inner { + kind: kind, + state: state, + })) + } + } +} + +#[macro_export] +#[doc(hidden)] +#[cfg(not(feature = "boxed-error"))] +macro_rules! impl_chained_error_new { + ($error_name: ident, $error_kind_name: ident) => { + fn new(kind: $error_kind_name, state: $crate::State) -> $error_name { + $error_name(Inner { + kind: kind, + state: state, + }) + } + } +} + +#[macro_export] +#[doc(hidden)] +#[cfg(feature = "boxed-error")] +macro_rules! define_error { + ($error_name: ident, $inner_name: ident) => { + /// The Error type. + /// + /// This struct is made of three things: + /// + /// - an `ErrorKind` which is used to determine the type of the error. + /// - a backtrace, generated when the error is created. + /// - an error chain, used for the implementation of `Error::cause()`. + #[derive(Debug)] + pub struct $error_name(pub Box<$inner_name>); + } +} + +#[macro_export] +#[doc(hidden)] +#[cfg(not(feature = "boxed-error"))] +macro_rules! define_error { + ($error_name: ident, $inner_name: ident) => { + /// The Error type. + /// + /// This struct is made of three things: + /// + /// - an `ErrorKind` which is used to determine the type of the error. + /// - a backtrace, generated when the error is created. + /// - an error chain, used for the implementation of `Error::cause()`. + #[derive(Debug)] + pub struct $error_name(pub $inner_name); + } +} diff --git a/src/lib.rs b/src/lib.rs index 63727c50b..7a4a3e60f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -269,7 +269,7 @@ pub type Backtrace = (); mod quick_error; #[macro_use] mod error_chain; -#[cfg(feature = "example_generated")] +#[cfg(feature = "example-generated")] pub mod example_generated; /// Iterator over the error chain using the `Error::cause()` method. @@ -320,18 +320,18 @@ pub trait ChainedError: error::Error + Send + 'static { } /// Additionnal methods for `Result`, for easy interaction with this crate. -pub trait ResultExt { +pub trait ResultExt { /// 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. - fn chain_err(self, callback: F) -> Result + fn chain_err(self, callback: F) -> Result where F: FnOnce() -> EK, EK: Into; } -impl ResultExt for Result where CE: ChainedError, E: error::Error + Send + 'static { - fn chain_err(self, callback: F) -> Result +impl ResultExt for Result where E: error::Error + Send + 'static { + fn chain_err(self, callback: F) -> Result where F: FnOnce() -> EK, EK: Into { self.map_err(move |e| {