commit 6066a41f974c49fc4f5c07293055893239819ed9 Author: Harald Hoyer Date: Tue Dec 18 07:46:05 2018 +0100 first commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6936990 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +**/*.rs.bk +Cargo.lock diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml new file mode 100644 index 0000000..accd0a4 --- /dev/null +++ b/.idea/codeStyles/Project.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..43136c6 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "chainerror" +version = "0.1.0" +authors = ["Harald Hoyer "] +edition = "2018" + diff --git a/README.md b/README.md new file mode 100644 index 0000000..f83ce69 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +# chainerror diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..10e7022 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,196 @@ +pub trait ChainError: ::std::error::Error + Sized { + fn root_cause(&self) -> Option<&(dyn::std::error::Error + 'static)>; + fn find_cause( + &self, + ) -> Option<&(dyn::std::error::Error + 'static)>; +} + +#[macro_export] +macro_rules! chain_error { + ( $t:ident, $v:expr $(, $more:expr)* ) => { + |e| <$t> :: new(line!(), file!(), format!($v, $( $more , )* ), Some(e.into())) + }; + ( $t:path, $v:expr $(, $more:expr)* ) => { + |e| <$t> :: new(line!(), file!(), format!($v, $( $more , )* ), 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 , )* ))) + }; + ( $v:expr $(, $more:expr)* ) => { + |e| e.into_chain_error(line!(), file!(), Some(format!($v, $( $more , )* ))) + }; + ( ) => { + |e| e.into_chain_error(line!(), file!(), None) + }; +} + +#[macro_export] +macro_rules! throw_error { + ( $t:ident, $v:expr $(, $more:expr)* ) => { + Err(<$t> :: new(line!(), file!(), format!($v, $( $more , )*), None)) + }; + ( $t:path, $v:expr $(, $more:expr)* ) => { + Err(<$t> :: new(line!(), file!(), format!($v, $( $more , )*), None)) + }; +} + +#[macro_export] +macro_rules! new_chain_error { + ($e:ident) => { + pub struct $e { + line: u32, + filename: &'static str, + description: String, + error_cause: Option>, + } + + impl $e { + pub fn new( + line: u32, + filename: &'static str, + description: String, + error_cause: Option>, + ) -> Self { + $e { + line, + filename, + description, + 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); + while let Some(c) = cause.source() { + cause = c; + } + Some(cause) + } + + fn find_cause( + &self, + ) -> Option<&(dyn::std::error::Error + 'static)> { + let mut cause = self as &(dyn::std::error::Error + 'static); + loop { + if cause.is::() { + return Some(cause); + } + + match cause.source() { + Some(c) => cause = c, + None => return None, + } + } + } + } + + impl ::std::error::Error for $e { + fn description(&self) -> &str { + &self.description + } + + fn source(&self) -> Option<&(dyn::std::error::Error + 'static)> { + if let Some(ref e) = self.error_cause { + Some(e.as_ref()) + } else { + None + } + } + } + + impl ::std::fmt::Display for $e { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + writeln!(f, "{}", self.description)?; + if let Some(e) = self.source() { + writeln!(f, "\nCaused by:")?; + ::std::fmt::Display::fmt(&e, f)?; + } + Ok(()) + } + } + + 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)?; + if let Some(e) = self.source() { + writeln!(f, "\nCaused by:")?; + ::std::fmt::Debug::fmt(&e, f)?; + } + Ok(()) + } + } + }; +} + +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}; +} + +#[cfg(test)] +mod tests { + use crate::prelude::*; + use std::error::Error; + + new_chain_error!(MyError); + new_chain_error!(MyMainError); + + fn throw_error() -> Result<(), MyError> { + let directory = String::from("ldfhgdfkgjdf"); + ::std::fs::remove_dir(&directory).map_err(chain_error!( + MyError, + "Could not remove directory '{}'{}", + &directory, + "!" + ))?; + Ok(()) + } + + #[test] + fn it_works() -> Result<(), MyMainError> { + let res = throw_error().map_err(chain_error!(MyMainError, "I has an error.")); + + if let Err(my_err) = res { + if let Some(source) = my_err.source() { + assert!(source.is::()); + } + println!("\nRoot cause is {:#?}\n", my_err.root_cause()); + assert!(my_err.root_cause().unwrap().is::<::std::io::Error>()); + assert!(my_err.find_cause::<::std::io::Error>().is_some()); + + if my_err.find_cause::<::std::io::Error>().is_some() { + println!("Has cause io::Error"); + } + if my_err.find_cause::().is_some() { + println!("Has cause MyError"); + } + println!("-----------"); + println!("Display Error:\n{}", my_err); + println!("-----------"); + println!("Debug Error: \n{:#?}", my_err); + println!("-----------"); + }; + //res?; + Ok(()) + } +}