diff --git a/src/backtrace.rs b/src/backtrace.rs index d558ae814..912601f53 100644 --- a/src/backtrace.rs +++ b/src/backtrace.rs @@ -43,7 +43,7 @@ mod imp { }; ENABLED.store(enabled as usize + 1, Ordering::SeqCst); if !enabled { - return InternalBacktrace { backtrace: None } + return InternalBacktrace { backtrace: None }; } } 1 => return InternalBacktrace { backtrace: None }, diff --git a/src/error_chain.rs b/src/error_chain.rs index 9b95a77d3..e6852ae1b 100644 --- a/src/error_chain.rs +++ b/src/error_chain.rs @@ -2,14 +2,14 @@ #[doc(hidden)] #[macro_export] macro_rules! impl_error_chain_processed { - // Default values for `types`. + // Default values for `types` and `derive`. ( types {} $( $rest: tt )* ) => { impl_error_chain_processed! { types { - Error, ErrorKind, ResultExt, Result; + Error, ErrorKind, ResultExt, Result, State, Trait; } $( $rest )* } @@ -18,14 +18,16 @@ macro_rules! impl_error_chain_processed { ( types { $error_name:ident, $error_kind_name:ident, - $result_ext_name:ident, $result_name:ident; + $result_ext_name:ident, $result_name:ident, + $state_name:ident, $trait_name:ident; } $( $rest: tt )* ) => { impl_error_chain_processed! { types { $error_name, $error_kind_name, - $result_ext_name; + $result_ext_name, $state_name, + $trait_name; } $( $rest )* } @@ -37,11 +39,13 @@ macro_rules! impl_error_chain_processed { ( types { $error_name:ident, $error_kind_name:ident, - $result_ext_name:ident; + $result_ext_name:ident, $state_name:ident, + $trait_name:ident; } links { - $( $link_variant:ident ( $link_error_path:path, $link_kind_path:path ) + $( $link_variant:ident ( $link_error_path:path, $link_kind_path:path, + $link_trait_path:path, $link_state_path:path ) $( #[$meta_links:meta] )*; ) * } @@ -54,7 +58,22 @@ macro_rules! impl_error_chain_processed { $( $error_chunks:tt ) * } + derive { + $($derive:ident, $bound:path);* + } ) => { + create_super_trait!($trait_name: ::std::fmt::Debug, ::std::error::Error, $crate::ToError, Send, + Sync $(, $bound)*); + + impl<'a> ::std::error::Error for &'a $trait_name { + fn cause(&self) -> Option<&::std::error::Error> { + (*self).cause() + } + fn description(&self) -> &str { + (*self).description() + } + } + /// The Error type. /// /// This tuple struct is made of two elements: @@ -64,6 +83,7 @@ macro_rules! impl_error_chain_processed { /// internals, containing: /// - a backtrace, generated when the error is created. /// - an error chain, used for the implementation of `Error::cause()`. + //#[derive(Debug, $($derive),*)] #[derive(Debug)] pub struct $error_name( // The members must be `pub` for `links`. @@ -71,13 +91,51 @@ macro_rules! impl_error_chain_processed { pub $error_kind_name, /// Contains the error chain and the backtrace. #[doc(hidden)] - pub $crate::State, + pub $crate::InternalState<$state_name>, ); - impl $crate::ChainedError for $error_name { + #[derive(Debug, $($derive),*)] + pub struct $state_name { + next_error: Option>, + } + + impl $state_name { + pub fn new(error: Box<$trait_name>) -> Self { + Self { + next_error: Some(error), + } + } + } + + impl Default for $state_name { + fn default() -> Self { + Self { + next_error: None, + } + } + } + impl $crate::State for $state_name { + type Error = $trait_name; + + fn new(error: Box) -> Self { + Self { + next_error: Some(error), + } + } + + fn next_error(&self) -> &Option> { + &self.next_error + } + + fn into_next_error(self) -> Option> { + self.next_error + } + } + + impl $crate::ChainedError<$state_name> for $error_name { type ErrorKind = $error_kind_name; - fn new(kind: $error_kind_name, state: $crate::State) -> $error_name { + fn new(kind: $error_kind_name, state: $crate::InternalState<$state_name>) -> $error_name { $error_name(kind, state) } @@ -87,7 +145,7 @@ macro_rules! impl_error_chain_processed { fn with_chain(error: E, kind: K) -> Self - where E: ::std::error::Error + Send + 'static, + where E: $crate::ToError + ::std::error::Error + Send + Sync + 'static, K: Into { Self::with_chain(error, kind) @@ -122,27 +180,27 @@ macro_rules! impl_error_chain_processed { pub fn from_kind(kind: $error_kind_name) -> $error_name { $error_name( kind, - $crate::State::default(), + $crate::InternalState::default(), ) } /// Constructs a chained error from another error and a kind, and generates a backtrace. pub fn with_chain(error: E, kind: K) -> $error_name - where E: ::std::error::Error + Send + 'static, + where E: $trait_name + 'static, K: Into<$error_kind_name> { $error_name::with_boxed_chain(Box::new(error), kind) } /// Construct a chained error from another boxed error and a kind, and generates a backtrace - pub fn with_boxed_chain(error: Box<::std::error::Error + Send>, kind: K) + pub fn with_boxed_chain(error: Box<$trait_name>, kind: K) -> $error_name where K: Into<$error_kind_name> { $error_name( kind.into(), - $crate::State::new::<$error_name>(error, ), + $crate::InternalState::new::<$error_name>(error), ) } @@ -158,7 +216,7 @@ macro_rules! impl_error_chain_processed { /// Returns the backtrace associated with this error. pub fn backtrace(&self) -> Option<&$crate::Backtrace> { - self.1.backtrace() + self.1.backtrace.as_backtrace() } /// Extends the error chain with a new entry. @@ -181,20 +239,26 @@ macro_rules! impl_error_chain_processed { #[allow(unknown_lints, unused_doc_comment)] fn cause(&self) -> Option<&::std::error::Error> { - match self.1.next_error { - Some(ref c) => Some(&**c), + let state = &self.1; + let error; + let next_error = state.next_error(); + + match *next_error { + Some(ref c) => error = Some(&*c.to_error()), None => { match self.0 { $( $(#[$meta_foreign_links])* $error_kind_name::$foreign_link_variant(ref foreign_err) => { - foreign_err.cause() + error = foreign_err.cause() } ) * - _ => None + _ => error = None } } } + + error } } @@ -205,12 +269,23 @@ macro_rules! impl_error_chain_processed { } $( + $(#[$meta_links])* + impl From<$link_state_path> for $state_name { + fn from(e: $link_state_path) -> Self { + Self { + next_error: $crate::State::into_next_error(e).map(|e| unsafe { + Box::new(e) as Box<$trait_name> + }) + } + } + } + $(#[$meta_links])* 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, + ::InternalState { inner: e.1.inner.into(), backtrace: e.1.backtrace }, ) } } @@ -251,7 +326,7 @@ macro_rules! impl_error_chain_processed { impl_error_chain_kind! { /// The kind of an error. - #[derive(Debug)] + #[derive(Debug, $($derive),*)] pub enum $error_kind_name { /// A convenient variant for String. @@ -320,12 +395,15 @@ macro_rules! impl_error_chain_processed { EK: Into<$error_kind_name>; } - impl $result_ext_name for ::std::result::Result where E: ::std::error::Error + Send + 'static { + impl $result_ext_name for ::std::result::Result + where E: $trait_name + Sync + 'static, + $state_name: $crate::State, + { fn chain_err(self, callback: F) -> ::std::result::Result where F: FnOnce() -> EK, EK: Into<$error_kind_name> { self.map_err(move |e| { - let state = $crate::State::new::<$error_name>(Box::new(e), ); + let state = $crate::InternalState::new::<$error_name>(Box::new(e)); $crate::ChainedError::new(callback().into(), state) }) } @@ -340,8 +418,6 @@ macro_rules! impl_error_chain_processed { }) } } - - }; } @@ -350,51 +426,62 @@ macro_rules! impl_error_chain_processed { #[macro_export] macro_rules! error_chain_processing { ( - ({}, $b:tt, $c:tt, $d:tt) + ({}, $b:tt, $c:tt, $d:tt, $e:tt) types $content:tt $( $tail:tt )* ) => { error_chain_processing! { - ($content, $b, $c, $d) + ($content, $b, $c, $d, $e) $($tail)* } }; ( - ($a:tt, {}, $c:tt, $d:tt) + ($a:tt, {}, $c:tt, $d:tt, $e:tt) links $content:tt $( $tail:tt )* ) => { error_chain_processing! { - ($a, $content, $c, $d) + ($a, $content, $c, $d, $e) $($tail)* } }; ( - ($a:tt, $b:tt, {}, $d:tt) + ($a:tt, $b:tt, {}, $d:tt, $e:tt) foreign_links $content:tt $( $tail:tt )* ) => { error_chain_processing! { - ($a, $b, $content, $d) + ($a, $b, $content, $d, $e) $($tail)* } }; ( - ($a:tt, $b:tt, $c:tt, {}) + ($a:tt, $b:tt, $c:tt, {}, $e:tt) errors $content:tt $( $tail:tt )* ) => { error_chain_processing! { - ($a, $b, $c, $content) + ($a, $b, $c, $content, $e) $($tail)* } }; - ( ($a:tt, $b:tt, $c:tt, $d:tt) ) => { + ( + ($a:tt, $b:tt, $c:tt, $d:tt, {}) + derive $content:tt + $( $tail:tt )* + ) => { + error_chain_processing! { + ($a, $b, $c, $d, $content) + $($tail)* + } + }; + ( ($a:tt, $b:tt, $c:tt, $d:tt, $e:tt) ) => { impl_error_chain_processed! { types $a links $b foreign_links $c errors $d + derive $e } }; } @@ -404,12 +491,22 @@ macro_rules! error_chain_processing { macro_rules! error_chain { ( $( $block_name:ident { $( $block_content:tt )* } )* ) => { error_chain_processing! { - ({}, {}, {}, {}) + ({}, {}, {}, {}, {}) $($block_name { $( $block_content )* })* } }; } +/// Macro used to generate traits with `Self` bounds +#[macro_export] +#[doc(hidden)] +macro_rules! create_super_trait { + ($name:ident: $bound_1:path, $($rest:path),*) => { + pub trait $name: $bound_1 $(+ $rest)* {} + impl $name for T {} +}; +} + /// Macro used to manage the `backtrace` feature. /// /// See diff --git a/src/example_generated.rs b/src/example_generated.rs index 29762eab4..92de92c67 100644 --- a/src/example_generated.rs +++ b/src/example_generated.rs @@ -20,19 +20,32 @@ //! arise frequently. /// Another code generated by the macro. + pub mod inner { - error_chain!{} + error_chain! { + derive { + //PartialEq, PartialEq + } + } } error_chain! { links { - Inner(inner::Error, inner::ErrorKind) #[doc = "Link to another `ErrorChain`."]; + Inner(inner::Error, inner::ErrorKind, inner::Trait, inner::State) #[doc = "Link to another `ErrorChain`."]; } foreign_links { - Io(::std::io::Error) #[doc = "Link to a `std::io::Error` type."]; + //Io(::std::io::Error) #[doc = "Link to a `std::error::Error` type."]; } errors { #[doc = "A custom error kind."] Custom } + derive { + //PartialEq, PartialEq + } } + +//fn foo() {} +//fn bar() { +//foo::(); +//} diff --git a/src/lib.rs b/src/lib.rs index ac185c643..fa0659883 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -#![deny(missing_docs)] +//#![deny(missing_docs)] #![allow(unknown_lints)] // to be removed when unused_doc_comments lints is merged #![doc(html_root_url = "https://docs.rs/error-chain/0.11.0")] @@ -360,7 +360,6 @@ //! mod utils { //! error_chain! { //! errors { -//! BadStuff { //! description("bad stuff") //! } //! } @@ -539,6 +538,7 @@ use std::error; use std::iter::Iterator; use std::fmt; +use std::marker::PhantomData; #[macro_use] mod impl_error_chain_kind; @@ -581,18 +581,21 @@ impl<'a> Iterator for Iter<'a> { /// This trait is implemented on all the errors generated by the `error_chain` /// macro. -pub trait ChainedError: error::Error + Send + 'static { +pub trait ChainedError: error::Error + Send + Sync + 'static { /// Associated kind type. type ErrorKind; /// Constructs an error from a kind, and generates a backtrace. - fn from_kind(kind: Self::ErrorKind) -> Self where Self: Sized; + fn from_kind(kind: Self::ErrorKind) -> Self + where + Self: Sized; /// Constructs a chained error from another error and a kind, and generates a backtrace. fn with_chain(error: E, kind: K) -> Self - where Self: Sized, - E: ::std::error::Error + Send + 'static, - K: Into; + where + Self: Sized, + E: ToError + ::std::error::Error + Send + Sync + 'static, + K: Into; /// Returns the kind of the error. fn kind(&self) -> &Self::ErrorKind; @@ -607,32 +610,52 @@ pub trait ChainedError: error::Error + Send + 'static { /// context of this error. /// /// The full cause chain and backtrace, if present, will be printed. - fn display_chain<'a>(&'a self) -> DisplayChain<'a, Self> { - DisplayChain(self) + fn display_chain<'a>(&'a self) -> DisplayChain<'a, Self, S> { + DisplayChain(self, PhantomData) } /// Extends the error chain with a new entry. fn chain_err(self, error: F) -> Self - where F: FnOnce() -> EK, - EK: Into; + where + F: FnOnce() -> EK, + EK: Into; /// Creates an error from its parts. #[doc(hidden)] - fn new(kind: Self::ErrorKind, state: State) -> Self where Self: Sized; + fn new(kind: Self::ErrorKind, state: InternalState) -> Self + where + Self: Sized; /// Returns the first known backtrace, either from its State or from one /// of the errors from `foreign_links`. #[doc(hidden)] - fn extract_backtrace(e: &(error::Error + Send + 'static)) -> Option - where Self: Sized; + fn extract_backtrace(e: &(error::Error + Send + 'static)) -> Option; } +pub trait ToError { + fn to_error(&self) -> &error::Error + where Self: error::Error + { + self + } + + fn to_error_send(&self) -> &(error::Error + Send) + where Self: error::Error + Send + { + self + } +} + +impl ToError for T {} + /// A struct which formats an error for output. #[derive(Debug)] -pub struct DisplayChain<'a, T: 'a + ?Sized>(&'a T); +pub struct DisplayChain<'a, T: 'a + ?Sized, S: State>(&'a T, PhantomData); -impl<'a, T> fmt::Display for DisplayChain<'a, T> - where T: ChainedError +impl<'a, T, S> fmt::Display for DisplayChain<'a, T, S> +where + S: State, + T: ChainedError, { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { // Keep `try!` for 1.10 support @@ -650,39 +673,49 @@ impl<'a, T> fmt::Display for DisplayChain<'a, T> } } +pub trait State: Default { + type Error: ?Sized; + + fn new(error: Box) -> Self; + fn next_error(&self) -> &Option>; + fn into_next_error(self) -> Option>; +} /// Common state between errors. #[derive(Debug)] #[doc(hidden)] -pub struct State { - /// Next error in the error chain. - pub next_error: Option>, +pub struct InternalState { + /// The user's state type generated by the `error_chain` macro + pub inner: S, /// Backtrace for the current error. pub backtrace: InternalBacktrace, } -impl Default for State { - fn default() -> State { - State { - next_error: None, +impl Default for InternalState { + #[cfg(feature = "backtrace")] + fn default() -> Self { + InternalState { + inner: Default::default(), backtrace: InternalBacktrace::new(), } } } -impl State { +impl InternalState + where S::Error: error::Error + Send + Sync, +{ /// Creates a new State type - pub fn new(e: Box) -> State { - let backtrace = CE::extract_backtrace(&*e) - .unwrap_or_else(InternalBacktrace::new); - State { - next_error: Some(e), + pub fn new>(e: Box) -> InternalState { + let backtrace = CE::extract_backtrace(&e).unwrap_or_else(InternalBacktrace::new); + + InternalState { + inner: S::new(e), backtrace: backtrace, } } - /// Returns the inner backtrace if present. - pub fn backtrace(&self) -> Option<&Backtrace> { - self.backtrace.as_backtrace() + /// Returns the next error in the error chain + pub fn next_error(&self) -> &Option> { + self.inner.next_error() } }