Going back to std
Not using chainerror
and going full std
would look like this:
Btw, the code size is bigger than using chainerror
:-)
#![allow(clippy::single_match)] #![allow(clippy::redundant_pattern_matching)] pub mod mycrate { use std::error::Error as StdError; use self::func2mod::{do_some_io, func2}; pub mod func2mod { use std::error::Error as StdError; use std::io; pub enum ErrorKind { IO(String), } impl std::fmt::Display for ErrorKind { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { match self { ErrorKind::IO(s) => std::fmt::Display::fmt(s, f), } } } impl std::fmt::Debug for ErrorKind { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { match self { ErrorKind::IO(s) => std::fmt::Display::fmt(s, f), } } } macro_rules! mcontext { ( $k:expr ) => {{ |e| { Error( $k, Some(Box::from(e)), Some(concat!(file!(), ":", line!(), ": ")), ) } }}; } pub struct Error( ErrorKind, Option<Box<dyn std::error::Error + 'static>>, Option<&'static str>, ); impl Error { pub fn kind(&self) -> &ErrorKind { &self.0 } } impl From<ErrorKind> for Error { fn from(e: ErrorKind) -> Self { Error(e, None, None) } } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.1.as_ref().map(|e| e.as_ref()) } } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::Display::fmt(&self.0, f) } } impl std::fmt::Debug for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { if let Some(ref o) = self.2 { std::fmt::Display::fmt(o, f)?; } std::fmt::Debug::fmt(&self.0, f)?; if let Some(e) = self.source() { std::fmt::Display::fmt("\nCaused by:\n", f)?; std::fmt::Debug::fmt(&e, f)?; } Ok(()) } } pub fn do_some_io() -> std::result::Result<(), Box<dyn std::error::Error>> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } pub fn func2() -> std::result::Result<(), Error> { let filename = "foo.txt"; do_some_io().map_err(mcontext!(ErrorKind::IO(format!( "Error reading '{}'", filename ))))?; Ok(()) } } #[derive(Debug)] pub enum ErrorKind { Func2, IO(String), } impl std::fmt::Display for ErrorKind { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result { match self { ErrorKind::Func2 => write!(f, "func1 error calling func2"), ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename), } } } macro_rules! mcontext { ( $k:expr ) => {{ |e| { Error( $k, Some(Box::from(e)), Some(concat!(file!(), ":", line!(), ": ")), ) } }}; } pub struct Error( ErrorKind, Option<Box<dyn std::error::Error + 'static>>, Option<&'static str>, ); impl Error { pub fn kind(&self) -> &ErrorKind { &self.0 } } impl From<ErrorKind> for Error { fn from(e: ErrorKind) -> Self { Error(e, None, None) } } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { self.1.as_ref().map(|e| e.as_ref()) } } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { std::fmt::Display::fmt(&self.0, f) } } impl std::fmt::Debug for Error { fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { if let Some(ref o) = self.2 { std::fmt::Display::fmt(o, f)?; } std::fmt::Debug::fmt(&self.0, f)?; if let Some(e) = self.source() { std::fmt::Display::fmt("\nCaused by:\n", f)?; std::fmt::Debug::fmt(&e, f)?; } Ok(()) } } pub type Result<T> = std::result::Result<T, Error>; pub fn func1() -> Result<()> { func2().map_err(mcontext!(ErrorKind::Func2))?; let filename = String::from("bar.txt"); do_some_io().map_err(mcontext!(ErrorKind::IO(filename)))?; Ok(()) } } fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> { use mycrate::func1; use mycrate::ErrorKind; use std::error::Error; use std::io; if let Err(e) = func1() { match e.kind() { 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: &dyn Error = &e; while let Some(c) = s.source() { if let Some(ioerror) = c.downcast_ref::<io::Error>() { 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); std::process::exit(1); } Ok(()) }