diff --git a/Cargo.toml b/Cargo.toml index d07ba7e..327757b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "chainerror" -version = "0.4.4" +version = "0.5.0" authors = ["Harald Hoyer "] edition = "2018" license = "MIT/Apache-2.0" diff --git a/examples/example.rs b/examples/example.rs index 8aadc8d..c383000 100644 --- a/examples/example.rs +++ b/examples/example.rs @@ -1,8 +1,9 @@ -use chainerror::*; use std::error::Error; use std::io; use std::result::Result; +use chainerror::*; + fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) diff --git a/examples/tutorial13.rs b/examples/tutorial13.rs index 9cfe090..78dee31 100644 --- a/examples/tutorial13.rs +++ b/examples/tutorial13.rs @@ -15,7 +15,7 @@ pub mod mycrate { Ok(()) } - #[derive(Debug)] + #[derive(Debug, Clone)] pub enum ErrorKind { Func2, IO(String), @@ -42,7 +42,7 @@ pub mod mycrate { } } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { use mycrate::func1; use mycrate::ErrorKind; use std::error::Error; diff --git a/examples/tutorial14.rs b/examples/tutorial14.rs index 5ccd7ce..e73d747 100644 --- a/examples/tutorial14.rs +++ b/examples/tutorial14.rs @@ -182,7 +182,7 @@ pub mod mycrate { } } -fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { use mycrate::func1; use mycrate::ErrorKind; use std::error::Error; diff --git a/examples/tutorial15.rs b/examples/tutorial15.rs new file mode 100644 index 0000000..c0f5fe7 --- /dev/null +++ b/examples/tutorial15.rs @@ -0,0 +1,139 @@ +pub mod mycrate { + use std::io; + + use chainerror::*; + + fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { + Err(io::Error::from(io::ErrorKind::NotFound))?; + Ok(()) + } + + derive_str_cherr!(Func2Error); + + fn func2() -> std::result::Result<(), Box> { + let filename = "foo.txt"; + do_some_io(filename) + .map_err(|e| cherr!(e, Func2Error(format!("Error reading '{}'", filename))))?; + Ok(()) + } + + #[derive(Debug, Clone)] + pub enum ErrorKind { + Func2, + IO(String), + FatalError(String), + Unknown, + } + + derive_err_kind!(Error, ErrorKind); + pub type Result = std::result::Result; + + impl std::fmt::Display for ErrorKind { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { + match self { + ErrorKind::FatalError(e) => write!(f, "fatal error {}", e), + ErrorKind::Unknown => write!(f, "unknown error"), + ErrorKind::Func2 => write!(f, "func1 error calling func2"), + ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), + } + } + } + + impl ErrorKind { + fn from_io_error(e: &io::Error, f: String) -> Self { + match e.kind() { + io::ErrorKind::BrokenPipe => panic!("Should not happen"), + io::ErrorKind::ConnectionReset => { + ErrorKind::FatalError(format!("While reading `{}`: {}", f, e)) + } + _ => ErrorKind::IO(f), + } + } + } + + impl From<&(dyn std::error::Error + 'static + Send + Sync)> for ErrorKind { + fn from(e: &(dyn std::error::Error + 'static + Send + Sync)) -> Self { + if let Some(_) = e.downcast_ref::() { + ErrorKind::IO(String::from("Unknown filename")) + } else if let Some(_) = e.downcast_inner_ref::() { + ErrorKind::Func2 + } else { + ErrorKind::Unknown + } + } + } + + impl From<&std::boxed::Box> for ErrorKind { + fn from(e: &std::boxed::Box) -> Self { + Self::from(&**e) + } + } + + impl From<&Func2Error> for ErrorKind { + fn from(_: &Func2Error) -> Self { + ErrorKind::Func2 + } + } + + impl From<&io::Error> for ErrorKind { + fn from(e: &io::Error) -> Self { + ErrorKind::IO(format!("{}", e)) + } + } + + pub fn func1() -> Result<()> { + func2().map_err(|e| cherr!(e, ErrorKind::from(&e)))?; + + let filename = "bar.txt"; + + do_some_io(filename) + .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?; + do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?; + do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?; + do_some_io(filename).map_err(minto_cherr!(ErrorKind))?; + + Ok(()) + } + + pub fn super_func1() -> Result<()> { + func1().map_err(minto_cherr!(ErrorKind))?; + Ok(()) + } + +} + +fn main() -> Result<(), Box> { + use mycrate::super_func1; + use mycrate::ErrorKind; + use std::error::Error; + use std::io; + + if let Err(e) = super_func1() { + match e.kind() { + ErrorKind::FatalError(f) => eprintln!("Main Error Report: Fatal Error: {}", f), + ErrorKind::Unknown => eprintln!("Main Error Report: Unknown error occurred"), + ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"), + ErrorKind::IO(ref filename) => { + eprintln!("Main Error Report: func1 error reading '{}'", filename) + } + } + + eprintln!(); + let mut s: &Error = &e; + while let Some(c) = s.source() { + if let Some(ioerror) = c.downcast_ref::() { + eprintln!("caused by: std::io::Error: {}", ioerror); + match ioerror.kind() { + io::ErrorKind::NotFound => eprintln!("of kind: std::io::ErrorKind::NotFound"), + _ => {} + } + } else { + eprintln!("caused by: {}", c); + } + s = c; + } + + eprintln!("\nDebug Error:\n{:?}", e); + } + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 2107335..f378585 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -174,7 +174,6 @@ keyword_idents, macro_use_extern_crate, missing_debug_implementations, - missing_docs, trivial_numeric_casts, unused_extern_crates, unused_import_braces, @@ -243,10 +242,10 @@ impl ChainError { /// # Examples /// /// ```rust - /// # use crate::chainerror::*; - /// # use std::error::Error; - /// # use std::io; - /// # use std::result::Result; + /// use chainerror::*; + /// use std::error::Error; + /// use std::io; + /// /// fn do_some_io() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) @@ -296,7 +295,7 @@ impl ChainError { /// # Examples /// /// ```rust - /// # use chainerror::{ChainError, derive_str_cherr}; + /// # use chainerror::*; /// # derive_str_cherr!(FooError); /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing @@ -319,7 +318,7 @@ impl ChainError { /// # Examples /// /// ```rust - /// # use chainerror::{ChainError, derive_str_cherr}; + /// # use chainerror::*; /// # derive_str_cherr!(FooErrorKind); /// # let err = ChainError::new(String::new(), None, None); /// // Instead of writing @@ -348,10 +347,10 @@ impl ChainError { /// # Examples /// /// ```rust - /// # use crate::chainerror::*; - /// # use std::error::Error; - /// # use std::io; - /// # use std::result::Result; + /// use chainerror::*; + /// use std::error::Error; + /// use std::io; + /// /// fn do_some_io() -> Result<(), Box> { /// Err(io::Error::from(io::ErrorKind::NotFound))?; /// Ok(()) @@ -449,6 +448,10 @@ pub trait ChainErrorDown { fn downcast_chain_ref(&self) -> Option<&ChainError>; /// Downcast to a mutable reference of `ChainError` fn downcast_chain_mut(&mut self) -> Option<&mut ChainError>; + /// Downcast to T of `ChainError` + fn downcast_inner_ref(&self) -> Option<&T>; + /// Downcast to T mutable reference of `ChainError` + fn downcast_inner_mut(&mut self) -> Option<&mut T>; } impl ChainErrorDown for ChainError { @@ -482,6 +485,31 @@ impl ChainErrorDown for ChainError { None } } + #[inline] + fn downcast_inner_ref(&self) -> Option<&T> { + if self.is_chain::() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&(*(self as *const dyn Error as *const &ChainError)).kind) + } + } else { + None + } + } + + #[inline] + fn downcast_inner_mut(&mut self) -> Option<&mut T> { + if self.is_chain::() { + #[allow(clippy::cast_ptr_alignment)] + unsafe { + #[allow(trivial_casts)] + Some(&mut (*(self as *mut dyn Error as *mut &mut ChainError)).kind) + } + } else { + None + } + } } impl ChainErrorDown for dyn Error + 'static { @@ -499,6 +527,22 @@ impl ChainErrorDown for dyn Error + 'static { fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { self.downcast_mut::>() } + + #[inline] + fn downcast_inner_ref(&self) -> Option<&T> { + self.downcast_ref::() + .or_else(|| self.downcast_ref::>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut(&mut self) -> Option<&mut T> { + if self.is::() { + return self.downcast_mut::(); + } + + self.downcast_mut::>() + .and_then(|e| e.downcast_inner_mut::()) + } } impl ChainErrorDown for dyn Error + 'static + Send { @@ -516,6 +560,22 @@ impl ChainErrorDown for dyn Error + 'static + Send { fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { self.downcast_mut::>() } + + #[inline] + fn downcast_inner_ref(&self) -> Option<&T> { + self.downcast_ref::() + .or_else(|| self.downcast_ref::>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut(&mut self) -> Option<&mut T> { + if self.is::() { + return self.downcast_mut::(); + } + + self.downcast_mut::>() + .and_then(|e| e.downcast_inner_mut::()) + } } impl ChainErrorDown for dyn Error + 'static + Send + Sync { @@ -533,26 +593,48 @@ impl ChainErrorDown for dyn Error + 'static + Send + Sync { fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { self.downcast_mut::>() } + + #[inline] + fn downcast_inner_ref(&self) -> Option<&T> { + self.downcast_ref::() + .or_else(|| self.downcast_ref::>().map(|e| e.kind())) + } + + #[inline] + fn downcast_inner_mut(&mut self) -> Option<&mut T> { + if self.is::() { + return self.downcast_mut::(); + } + + self.downcast_mut::>() + .and_then(|e| e.downcast_inner_mut::()) + } } impl Error for ChainError { #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { - self.error_cause.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static)) + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) } } impl Error for &ChainError { #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { - self.error_cause.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static)) + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) } } impl Error for &mut ChainError { #[inline] fn source(&self) -> Option<&(dyn Error + 'static)> { - self.error_cause.as_ref().map(|e| e.as_ref() as &(dyn Error + 'static)) + self.error_cause + .as_ref() + .map(|e| e.as_ref() as &(dyn Error + 'static)) } } @@ -629,28 +711,34 @@ where #[inline] fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError { let e: U = t.into(); - ChainError::<_>::new(e, None, line_filename) + ChainError::new(e, None, line_filename) } } -/// map into `ChainError` with `IntoChainError` +/* +impl ChainErrorFrom for U + where + T: 'static + Error + Into> + Clone, + U: 'static + Display + Debug + From, +{ + #[inline] + fn chain_error_from(t: T, line_filename: Option<&'static str>) -> ChainError { + ChainError::new(U::from(t.clone()), Some(Box::from(t)), line_filename) + } +} +*/ + +/// map into `ChainError` with `T::from(err)` /// /// adds `line!()` and `file!()` information #[macro_export] macro_rules! minto_cherr { - ( ) => { - |e| ChainErrorFrom::chain_error_from(e, Some(concat!(file!(), ":", line!(), ": "))) - }; -} - -/// into `ChainError` with `IntoChainError` -/// -/// adds `line!()` and `file!()` information -#[macro_export] -macro_rules! into_cherr { - ( $t:expr ) => { - ChainErrorFrom::chain_error_from($t, Some(concat!(file!(), ":", line!(), ": "))) - }; + ( $k:ident ) => ( + |e| $crate::cherr!(e, $k::from(&e)) + ); + ( $enum:ident $(:: $enum_path:ident)* ) => ( + |e| $crate::cherr!(e, $enum $(:: $enum_path)*::from(&e)) + ); } /// Creates a new `ChainError` @@ -734,32 +822,23 @@ macro_rules! into_cherr { #[macro_export] macro_rules! cherr { ( $k:expr ) => ({ - ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) + $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) }); ( None, $k:expr ) => ({ - ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) + $crate::ChainError::new($k, None, Some(concat!(file!(), ":", line!(), ": "))) }); ( None, $fmt:expr, $($arg:tt)+ ) => ({ - cherr!(None, format!($fmt, $($arg)+ )) + $crate::cherr!(None, format!($fmt, $($arg)+ )) }); ( None, $fmt:expr, $($arg:tt)+ ) => ({ - cherr!(None, format!($fmt, $($arg)+ )) + $crate::cherr!(None, format!($fmt, $($arg)+ )) }); ( $e:path, $k:expr ) => ({ - ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": "))) + $crate::ChainError::new($k, Some(Box::from($e)), Some(concat!(file!(), ":", line!(), ": "))) }); ( $e:path, $fmt:expr, $($arg:tt)+ ) => ({ - cherr!($e, format!($fmt, $($arg)+ )) + $crate::cherr!($e, format!($fmt, $($arg)+ )) }); - -} - -/// shortcut for |e| cherr!(e, $k) -#[macro_export] -macro_rules! mcherr { - ( $k:expr ) => {{ - |e| cherr!(e, $k) - }}; } /// Convenience macro for `|e| cherr!(e, format!(…))` @@ -846,22 +925,22 @@ macro_rules! mcherr { #[macro_export] macro_rules! mstrerr { ( $t:path, $msg:expr ) => ({ - |e| cherr!(e, $t ($msg.to_string())) + |e| $crate::cherr!(e, $t ($msg.to_string())) }); ( $t:path, $msg:expr, ) => ({ - |e| cherr!(e, $t ($msg.to_string())) + |e| $crate::cherr!(e, $t ($msg.to_string())) }); ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ - |e| cherr!(e, $t (format!($fmt, $($arg)+ ))) + |e| $crate::cherr!(e, $t (format!($fmt, $($arg)+ ))) }); ($msg:expr) => ({ - |e| cherr!(e, $msg.to_string()) + |e| $crate::cherr!(e, $msg.to_string()) }); ($msg:expr, ) => ({ - |e| cherr!(e, $msg.to_string()) + |e| $crate::cherr!(e, $msg.to_string()) }); ($fmt:expr, $($arg:tt)+) => ({ - |e| cherr!(e, format!($fmt, $($arg)+ )) + |e| $crate::cherr!(e, format!($fmt, $($arg)+ )) }); } @@ -882,7 +961,7 @@ macro_rules! mstrerr { /// /// derive_str_cherr!(Func1Error); /// -/// fn func1() -> Result<(), Box> { +/// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } @@ -902,22 +981,22 @@ macro_rules! mstrerr { #[macro_export] macro_rules! strerr { ( $t:path, $msg:expr ) => ({ - cherr!($t ($msg.to_string())) + $crate::cherr!($t ($msg.to_string())) }); ( $t:path, $msg:expr, ) => ({ - cherr!($t ($msg.to_string())) + $crate::cherr!($t ($msg.to_string())) }); ( $t:path, $fmt:expr, $($arg:tt)+ ) => ({ - cherr!($t (format!($fmt, $($arg)+ ))) + $crate::cherr!($t (format!($fmt, $($arg)+ ))) }); ($msg:expr) => ({ - cherr!($msg.to_string()) + $crate::cherr!($msg.to_string()) }); ($msg:expr, ) => ({ - cherr!($msg.to_string()) + $crate::cherr!($msg.to_string()) }); ($fmt:expr, $($arg:tt)+) => ({ - cherr!(format!($fmt, $($arg)+ )) + $crate::cherr!(format!($fmt, $($arg)+ )) }); } @@ -944,7 +1023,7 @@ macro_rules! strerr { /// /// derive_str_cherr!(Func1Error); /// -/// fn func1() -> Result<(), Box> { +/// fn func1() -> Result<(), Box> { /// func2().map_err(mstrerr!(Func1Error, "func1 error"))?; /// Ok(()) /// } @@ -980,13 +1059,76 @@ macro_rules! derive_str_cherr { }; } -/// Derive an Error, which wraps ChainError and implements a kind() method +/// Derive an Error for an ErrorKind, which wraps a `ChainError` and implements a `kind()` method /// -/// e.kind() returns the kind +/// It basically hides `ChainError` to the outside and only exposes the `kind()` +/// method. +/// +/// Error::kind() returns the ErrorKind +/// Error::source() returns the parent error +/// +/// # Examples +/// +/// ```rust +/// use std::io; +/// use chainerror::*; +/// +/// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> { +/// return Err(io::Error::from(io::ErrorKind::NotFound)); +/// } +/// +/// #[derive(Debug, Clone)] +/// pub enum ErrorKind { +/// IO(String), +/// FatalError(String), +/// Unknown, +/// } +/// +/// derive_err_kind!(Error, ErrorKind); +/// +/// impl std::fmt::Display for ErrorKind { +/// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { +/// match self { +/// ErrorKind::FatalError(e) => write!(f, "fatal error {}", e), +/// ErrorKind::Unknown => write!(f, "unknown error"), +/// ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), +/// } +/// } +/// } +/// +/// impl ErrorKind { +/// fn from_io_error(e: &io::Error, f: String) -> Self { +/// match e.kind() { +/// io::ErrorKind::BrokenPipe => panic!("Should not happen"), +/// io::ErrorKind::ConnectionReset => { +/// ErrorKind::FatalError(format!("While reading `{}`: {}", f, e)) +/// } +/// _ => ErrorKind::IO(f), +/// } +/// } +/// } +/// +/// impl From<&io::Error> for ErrorKind { +/// fn from(e: &io::Error) -> Self { +/// ErrorKind::IO(format!("{}", e)) +/// } +/// } +/// +/// pub fn func1() -> std::result::Result<(), Error> { +/// let filename = "bar.txt"; +/// +/// do_some_io(filename) +/// .map_err(|e| cherr!(e, ErrorKind::from_io_error(&e, filename.into())))?; +/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::IO(filename.into())))?; +/// do_some_io(filename).map_err(|e| cherr!(e, ErrorKind::from(&e)))?; +/// do_some_io(filename).map_err(minto_cherr!(ErrorKind))?; +/// Ok(()) +/// } +/// ``` #[macro_export] macro_rules! derive_err_kind { ($e:ident, $k:ident) => { - pub struct $e(ChainError<$k>); + pub struct $e($crate::ChainError<$k>); impl $e { pub fn kind(&self) -> &$k { @@ -996,16 +1138,38 @@ macro_rules! derive_err_kind { impl From<$k> for $e { fn from(e: $k) -> Self { - $e(ChainError::new(e, None, None)) + $e($crate::ChainError::new(e, None, None)) } } impl From> for $e { - fn from(e: ChainError<$k>) -> Self { + fn from(e: $crate::ChainError<$k>) -> Self { $e(e) } } + impl From<&$e> for $k + where + $k: Clone, + { + fn from(e: &$e) -> Self { + e.kind().clone() + } + } + + impl $crate::ChainErrorFrom<$e> for $k + where + $k: Clone, + { + #[inline] + fn chain_error_from( + t: $e, + line_filename: Option<&'static str>, + ) -> $crate::ChainError<$k> { + $crate::ChainError::new((*t.kind()).clone(), Some(Box::from(t)), line_filename) + } + } + impl std::error::Error for $e { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.0.source()