use std::error::Error; use std::fmt::{Debug, Display, Formatter, Result}; use std::result::Result as StdResult; pub struct ChainError { #[cfg(feature = "fileline")] occurrence: Option<(u32, &'static str)>, kind: T, error_cause: Option>, } pub type ChainResult = StdResult>; impl ChainError { #[cfg(feature = "fileline")] pub fn new( kind: T, error_cause: Option>, occurrence: Option<(u32, &'static str)>, ) -> Self { Self { occurrence, kind, error_cause, } } #[cfg(not(feature = "fileline"))] pub fn new( kind: T, error_cause: Option>, _occurrence: Option<(u32, &'static str)>, ) -> Self { Self { kind, error_cause } } pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { let mut cause = self as &(dyn Error + 'static); while let Some(c) = cause.source() { cause = c; } Some(cause) } pub fn find_cause(&self) -> Option<&U> { let mut cause = self as &(dyn Error + 'static); loop { if cause.is::() { return cause.downcast_ref::(); } match cause.source() { Some(c) => cause = c, None => return None, } } } pub fn find_chain_cause(&self) -> Option<&ChainError> { let mut cause = self as &(dyn Error + 'static); loop { if cause.is::>() { return cause.downcast_ref::>(); } match cause.source() { Some(c) => cause = c, None => return None, } } } pub fn kind<'a>(&'a self) -> &'a T { &self.kind } } pub trait ChainErrorDown { fn is_chain(&self) -> bool; fn downcast_chain_ref(&self) -> Option<&ChainError>; fn downcast_chain_mut(&mut self) -> Option<&mut ChainError>; } use std::any::TypeId; impl ChainErrorDown for ChainError { fn is_chain(&self) -> bool { TypeId::of::() == TypeId::of::() } fn downcast_chain_ref(&self) -> Option<&ChainError> { if self.is_chain::() { unsafe { Some(&*(self as *const dyn Error as *const &ChainError)) } } else { None } } fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { if self.is_chain::() { unsafe { Some(&mut *(self as *mut dyn Error as *mut &mut ChainError)) } } else { None } } } impl ChainErrorDown for dyn Error + 'static { fn is_chain(&self) -> bool { self.is::>() } fn downcast_chain_ref(&self) -> Option<&ChainError> { self.downcast_ref::>() } fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { self.downcast_mut::>() } } impl ChainErrorDown for dyn Error + 'static + Send { fn is_chain(&self) -> bool { self.is::>() } fn downcast_chain_ref(&self) -> Option<&ChainError> { self.downcast_ref::>() } fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { self.downcast_mut::>() } } impl ChainErrorDown for dyn Error + 'static + Send + Sync { fn is_chain(&self) -> bool { self.is::>() } fn downcast_chain_ref(&self) -> Option<&ChainError> { self.downcast_ref::>() } fn downcast_chain_mut(&mut self) -> Option<&mut ChainError> { self.downcast_mut::>() } } impl Error for ChainError { fn source(&self) -> Option<&(dyn Error + 'static)> { if let Some(ref e) = self.error_cause { Some(e.as_ref()) } else { None } } } impl Error for &ChainError { fn source(&self) -> Option<&(dyn Error + 'static)> { if let Some(ref e) = self.error_cause { Some(e.as_ref()) } else { None } } } impl Error for &mut ChainError { fn source(&self) -> Option<&(dyn Error + 'static)> { if let Some(ref e) = self.error_cause { Some(e.as_ref()) } else { None } } } impl Display for ChainError { fn fmt(&self, f: &mut Formatter) -> Result { write!(f, "{}", self.kind)?; #[cfg(feature = "display-cause")] { if let Some(e) = self.source() { writeln!(f, "\nCaused by:")?; Display::fmt(&e, f)?; } } Ok(()) } } impl Debug for ChainError { fn fmt(&self, f: &mut Formatter) -> Result { #[cfg(feature = "fileline")] { if let Some(o) = self.occurrence { write!(f, "{}:{}: ", o.1, o.0)?; } } Debug::fmt(&self.kind, f)?; #[cfg(feature = "debug-cause")] { if let Some(e) = self.source() { writeln!(f, "\nCaused by:")?; Debug::fmt(&e, f)?; } } Ok(()) } } #[macro_export] macro_rules! cherr { ( $k:expr ) => { ChainError::<_>::new($k, None, Some((line!(), file!()))) }; ( $e:expr, $k:expr ) => { ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) }; } #[macro_export] macro_rules! mstrerr { ( $t:ident, $v:expr $(, $more:expr)* ) => { |e| cherr!(e, $t (format!($v, $( $more , )* ))) }; ( $t:path, $v:expr $(, $more:expr)* ) => { |e| cherr!(e, $t (format!($v, $( $more , )* ))) }; ( $v:expr $(, $more:expr)* ) => { |e| cherr!(e, format!($v, $( $more , )* )) }; } #[macro_export] macro_rules! derive_str_cherr { ($e:ident) => { struct $e(String); impl ::std::fmt::Display for $e { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { write!(f, "{}", self.0) } } impl ::std::fmt::Debug for $e { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { write!(f, "{}({})", stringify!($e), self.0) } } impl ::std::error::Error for $e {} }; } #[macro_export] macro_rules! try_cherr_ref { ( $e:expr, $t:ident ) => { $e.downcast_ref::>() }; ( $e:expr, $t:path ) => { $e.downcast_ref::>() }; } #[macro_export] macro_rules! try_cherr_mut { ( $e:expr, $t:ident ) => { $e.downcast_mut::>() }; ( $e:expr, $t:path ) => { $e.downcast_mut::>() }; } pub mod prelude { pub use super::{ cherr, derive_str_cherr, mstrerr, try_cherr_mut, try_cherr_ref, ChainError, ChainErrorDown, ChainResult, }; } #[cfg(test)] mod tests { }