Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //////////////////////////////////////////////////////////////////////////////
- /// Crate chainerror
- //////////////////////////////////////////////////////////////////////////////
- use std::error::Error;
- use std::fmt::{self, Debug, Display, Formatter};
- pub struct ChainError<T> {
- occurrence: Option<(u32, &'static str)>,
- kind: T,
- error_cause: Option<Box<dyn Error + 'static>>,
- }
- impl<T: 'static + Display + Debug> ChainError<T> {
- pub fn new(
- kind: T,
- error_cause: Option<Box<dyn Error + 'static>>,
- occurrence: Option<(u32, &'static str)>,
- ) -> Self {
- Self {
- occurrence,
- 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<U: Error + 'static>(&self) -> Option<&(dyn Error + 'static)> {
- let mut cause = self as &(dyn Error + 'static);
- loop {
- if cause.is::<U>() {
- return Some(cause);
- }
- match cause.source() {
- Some(c) => cause = c,
- None => return None,
- }
- }
- }
- pub fn find_kind<U: 'static + Display + Debug>(&self) -> Option<&ChainError<U>> {
- let mut cause = self as &(dyn Error + 'static);
- loop {
- if cause.is::<ChainError<U>>() {
- return cause.downcast_ref::<ChainError<U>>();
- }
- match cause.source() {
- Some(c) => cause = c,
- None => return None,
- }
- }
- }
- pub fn kind<'a>(&'a self) -> &'a T {
- &self.kind
- }
- }
- impl<T: 'static + Display + Debug> Error for ChainError<T> {
- fn source(&self) -> Option<&(dyn Error + 'static)> {
- if let Some(ref e) = self.error_cause {
- Some(e.as_ref())
- } else {
- None
- }
- }
- }
- impl<T: 'static + Display + Debug> Display for ChainError<T> {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- write!(f, "{}", self.kind)?;
- if let Some(e) = self.source() {
- writeln!(f, "\nCaused by:")?;
- Display::fmt(&e, f)?;
- }
- Ok(())
- }
- }
- impl<T: 'static + Display + Debug> Debug for ChainError<T> {
- fn fmt(&self, f: &mut Formatter) -> fmt::Result {
- if let Some(o) = self.occurrence {
- write!(f, "{}:{}: ", o.1, o.0)?;
- }
- Debug::fmt(&self.kind, f)?;
- 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 , )* )))
- };
- }
- #[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)
- }
- }
- };
- }
- //////////////////////////////////////////////////////////////////////////////
- /// Example
- //////////////////////////////////////////////////////////////////////////////
- use std::io::Error as IoError;
- use std::io::ErrorKind as IoErrorKind;
- use std::path::Path;
- #[derive(Clone, PartialEq, Debug)]
- enum ParseError {
- InvalidValue(u32),
- InvalidParameter(String),
- NoOpen,
- NoClose,
- }
- impl ::std::fmt::Display for ParseError {
- fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
- match self {
- ParseError::InvalidValue(a) => write!(f, "InvalidValue: {}", a),
- ParseError::InvalidParameter(a) => write!(f, "InvalidParameter: '{}'", a),
- ParseError::NoOpen => write!(f, "No opening '{{' in config file"),
- ParseError::NoClose => write!(f, "No closing '}}' in config file"),
- }
- }
- }
- fn parse_config(c: String) -> Result<(), Box<Error>> {
- if !c.starts_with('{') {
- Err(cherr!(ParseError::NoOpen))?;
- }
- if !c.ends_with('}') {
- Err(cherr!(ParseError::NoClose))?;
- }
- let c = &c[1..(c.len() - 1)];
- let v = c
- .parse::<u32>()
- .map_err(|e| cherr!(e, ParseError::InvalidParameter(c.into())))?;
- if v > 100 {
- Err(cherr!(ParseError::InvalidValue(v)))?;
- }
- Ok(())
- }
- derive_str_cherr!(ConfigFileError);
- derive_str_cherr!(SeriousError);
- derive_str_cherr!(FileError);
- derive_str_cherr!(AppError);
- fn file_reader(filename: &Path) -> Result<(), Box<Error>> {
- Err(IoError::from(IoErrorKind::NotFound)).map_err(mstrerr!(
- FileError,
- "Can't find {:?}",
- filename
- ))?;
- Ok(())
- }
- fn read_config(filename: &Path) -> Result<(), Box<Error>> {
- if filename.eq(Path::new("global.ini")) {
- // assume we got an IO error
- file_reader(filename).map_err(mstrerr!(ConfigFileError, "Can't find {:?}", filename))?;
- }
- // assume we read some buffer
- if filename.eq(Path::new("local.ini")) {
- let buf = String::from("{1000}");
- parse_config(buf)?;
- }
- if filename.eq(Path::new("user.ini")) {
- let buf = String::from("foo");
- parse_config(buf)?;
- }
- if filename.eq(Path::new("user2.ini")) {
- let buf = String::from("{foo");
- parse_config(buf)?;
- }
- if filename.eq(Path::new("user3.ini")) {
- let buf = String::from("{foo}");
- parse_config(buf)?;
- }
- if filename.eq(Path::new("custom.ini")) {
- let buf = String::from("{10}");
- parse_config(buf)?;
- }
- if filename.eq(Path::new("essential.ini")) {
- Err(cherr!(SeriousError("Something is really wrong".into())))?;
- }
- Ok(())
- }
- fn read_verbose_config(p: &str) -> Result<(), ChainError<AppError>> {
- eprintln!("Reading '{}' ... ", p);
- read_config(Path::new(p)).map_err(mstrerr!(AppError, "{}", p))?;
- eprintln!("Ok reading {}", p);
- Ok(())
- }
- fn start_app(debug: bool) -> Result<(), Box<Error>> {
- for p in &[
- "global.ini",
- "local.ini",
- "user.ini",
- "user2.ini",
- "user3.ini",
- "custom.ini",
- "essential.ini",
- ] {
- if let Err(e) = read_verbose_config(p) {
- if e.find_kind::<SeriousError>().is_some() {
- // Bail out on SeriousError
- eprintln!("---> Serious Error:\n{:?}", e);
- Err(cherr!(e, AppError("Seriously".into())))?;
- } else if let Some(cfg_error) = e.find_kind::<ConfigFileError>() {
- if debug {
- eprintln!("{:?}\n", cfg_error);
- } else {
- // Deep Error handling
- if let Some(chioerror) = cfg_error.find_kind::<IoError>() {
- let ioerror = chioerror.kind();
- match ioerror.kind() {
- IoErrorKind::NotFound => {
- eprint!("Ignoring missing file");
- if let Some(root_cause) = cfg_error.root_cause() {
- eprint!(", because of: {}\n", root_cause);
- }
- eprintln!();
- }
- _ => Err(cherr!(e, AppError("Unhandled IOError".into())))?,
- }
- } else {
- eprintln!("ConfigFileError for: {}", e);
- }
- }
- } else {
- if debug {
- eprintln!("Error reading:\n{:?}\n", e)
- } else {
- eprintln!("Error reading: {}\n", e)
- }
- }
- }
- eprintln!();
- }
- Ok(())
- }
- fn main() {
- eprintln!("Display:\n");
- let e = start_app(false).unwrap_err();
- assert!(e.is::<ChainError<AppError>>());
- eprintln!("\n\n==================================");
- eprintln!("==== Debug output");
- eprintln!("==================================\n");
- let r = start_app(true);
- assert!(r.unwrap_err().is::<ChainError<AppError>>());
- }
Add Comment
Please, Sign In to add comment