diff --git a/src/lib.rs b/src/lib.rs index 10e7022..0c0c155 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,25 +1,52 @@ pub trait ChainError: ::std::error::Error + Sized { - fn root_cause(&self) -> Option<&(dyn::std::error::Error + 'static)>; + fn new( + line: u32, + filename: &'static str, + description: Option, + error_cause: Option>, + ) -> Self; + + fn root_cause(&self) -> Option<&(dyn std::error::Error + 'static)>; fn find_cause( &self, - ) -> Option<&(dyn::std::error::Error + 'static)>; + ) -> Option<&(dyn std::error::Error + 'static)>; +} + +pub trait ChainErrorFrom: ChainError { + fn chain_error_from(_: T, _: u32, _: &'static str, _: Option) -> Self; +} + +pub trait IntoChainError: Sized { + fn into_chain_error(self, line: u32, filename: &'static str, description: Option) -> T; +} + +impl IntoChainError for T +where + U: ChainErrorFrom + ChainError, +{ + fn into_chain_error(self, line: u32, filename: &'static str, description: Option) -> U { + U::chain_error_from(self, line, filename, description) + } } #[macro_export] -macro_rules! chain_error { +macro_rules! chain_error_fn { ( $t:ident, $v:expr $(, $more:expr)* ) => { - |e| <$t> :: new(line!(), file!(), format!($v, $( $more , )* ), Some(e.into())) + |e| <$t> :: new(line!(), file!(), Some(format!($v, $( $more , )* )), Some(e.into())) }; ( $t:path, $v:expr $(, $more:expr)* ) => { - |e| <$t> :: new(line!(), file!(), format!($v, $( $more , )* ), Some(e.into())) + |e| <$t> :: new(line!(), file!(), Some(format!($v, $( $more , )* )), Some(e.into())) + }; + ( $t:ident) => { + |e| <$t> :: new(line!(), file!(), None, Some(e.into())) + }; + ( $t:path) => { + |e| <$t> :: new(line!(), file!(), None, Some(e.into())) }; } #[macro_export] -macro_rules! into_chain_error { - ( $v:expr $(, $more:expr)* ) => { - |e| e.into_chain_error(line!(), file!(), Some(format!($v, $( $more , )* ))) - }; +macro_rules! into_chain_error_fn { ( $v:expr $(, $more:expr)* ) => { |e| e.into_chain_error(line!(), file!(), Some(format!($v, $( $more , )* ))) }; @@ -29,31 +56,52 @@ macro_rules! into_chain_error { } #[macro_export] -macro_rules! throw_error { - ( $t:ident, $v:expr $(, $more:expr)* ) => { - Err(<$t> :: new(line!(), file!(), format!($v, $( $more , )*), None)) +macro_rules! chain_error_from_fn { + ( $t:expr, $v:expr $(, $more:expr)* ) => { + |e| ($t).into().chain_error_from(e, line!(), file!(), Some(format!($v, $( $more , )* ))) }; - ( $t:path, $v:expr $(, $more:expr)* ) => { - Err(<$t> :: new(line!(), file!(), format!($v, $( $more , )*), None)) + + ( $t:expr ) => { + |e| ($t).into().chain_error_from(e, line!(), file!(), None) }; } #[macro_export] -macro_rules! new_chain_error { +macro_rules! chain_error { + ( $t:ident, $v:expr $(, $more:expr)* ) => { + <$t> :: new(line!(), file!(), Some(format!($v, $( $more , )*)), None) + }; + ( $t:path, $v:expr $(, $more:expr)* ) => { + <$t> :: new(line!(), file!(), Some(format!($v, $( $more , )*)), None) + }; +} + +#[macro_export] +macro_rules! into_chain_error { + ( $t:expr, $v:expr $(, $more:expr)* ) => { + $t . into_chain_error(line!(), file!(), Some(format!($v, $( $more , )*))) + }; + ( $t:expr ) => { + $t . into_chain_error(line!(), file!(), None) + }; +} + +#[macro_export] +macro_rules! derive_chain_error { ($e:ident) => { pub struct $e { line: u32, filename: &'static str, - description: String, - error_cause: Option>, + description: Option, + error_cause: Option>, } - impl $e { - pub fn new( + impl ChainError for $e { + fn new( line: u32, filename: &'static str, - description: String, - error_cause: Option>, + description: Option, + error_cause: Option>, ) -> Self { $e { line, @@ -62,11 +110,9 @@ macro_rules! new_chain_error { error_cause, } } - } - impl ChainError for $e { - fn root_cause(&self) -> Option<&(dyn::std::error::Error + 'static)> { - let mut cause = self as &(dyn::std::error::Error + 'static); + fn root_cause(&self) -> Option<&(dyn std::error::Error + 'static)> { + let mut cause = self as &(dyn std::error::Error + 'static); while let Some(c) = cause.source() { cause = c; } @@ -75,8 +121,8 @@ macro_rules! new_chain_error { fn find_cause( &self, - ) -> Option<&(dyn::std::error::Error + 'static)> { - let mut cause = self as &(dyn::std::error::Error + 'static); + ) -> Option<&(dyn std::error::Error + 'static)> { + let mut cause = self as &(dyn std::error::Error + 'static); loop { if cause.is::() { return Some(cause); @@ -92,10 +138,14 @@ macro_rules! new_chain_error { impl ::std::error::Error for $e { fn description(&self) -> &str { - &self.description + if let Some(ref d) = self.description { + d.as_ref() + } else { + "" + } } - fn source(&self) -> Option<&(dyn::std::error::Error + 'static)> { + fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { if let Some(ref e) = self.error_cause { Some(e.as_ref()) } else { @@ -106,7 +156,7 @@ macro_rules! new_chain_error { impl ::std::fmt::Display for $e { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - writeln!(f, "{}", self.description)?; + writeln!(f, "{}", self.description())?; if let Some(e) = self.source() { writeln!(f, "\nCaused by:")?; ::std::fmt::Display::fmt(&e, f)?; @@ -117,7 +167,13 @@ macro_rules! new_chain_error { impl ::std::fmt::Debug for $e { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - writeln!(f, "\n{}:{}: {}", self.filename, self.line, self.description)?; + writeln!( + f, + "\n{}:{}: {}", + self.filename, + self.line, + self.description() + )?; if let Some(e) = self.source() { writeln!(f, "\nCaused by:")?; ::std::fmt::Debug::fmt(&e, f)?; @@ -128,36 +184,25 @@ macro_rules! new_chain_error { }; } -pub trait FromChainError: Sized { - fn from_chain_error(_: T, _: u32, _: &'static str, _: Option) -> Self; -} - -pub trait IntoChainError: Sized { - fn into_chain_error(self, line: u32, filename: &'static str, description: Option) -> T; -} - -impl IntoChainError for T where U: FromChainError -{ - fn into_chain_error(self, line: u32, filename: &'static str, description: Option) -> U { - U::from_chain_error(self, line, filename, description) - } -} - pub mod prelude { - pub use super::{chain_error, new_chain_error, throw_error, ChainError}; + pub use super::{ + chain_error, chain_error_fn, chain_error_from_fn, derive_chain_error, into_chain_error, + into_chain_error_fn, ChainError, ChainErrorFrom, IntoChainError, + }; } #[cfg(test)] mod tests { - use crate::prelude::*; use std::error::Error; - new_chain_error!(MyError); - new_chain_error!(MyMainError); + use crate::prelude::*; + + derive_chain_error!(MyError); + derive_chain_error!(MyMainError); fn throw_error() -> Result<(), MyError> { let directory = String::from("ldfhgdfkgjdf"); - ::std::fs::remove_dir(&directory).map_err(chain_error!( + ::std::fs::remove_dir(&directory).map_err(chain_error_fn!( MyError, "Could not remove directory '{}'{}", &directory, @@ -168,7 +213,7 @@ mod tests { #[test] fn it_works() -> Result<(), MyMainError> { - let res = throw_error().map_err(chain_error!(MyMainError, "I has an error.")); + let res = throw_error().map_err(chain_error_fn!(MyMainError, "I has an error.")); if let Err(my_err) = res { if let Some(source) = my_err.source() {