ErrorKind to the rescue

To cope with different kind of errors, we introduce the kind of an error Func1ErrorKind with an enum.

Because we derive Debug and implement Display our Func1ErrorKind enum, this enum can be used as a std::error::Error.

Not using String errors anymore, the cherr!() macro seen in the beginning of the tutorial has to be used again.

Only returning Func1ErrorKind in func1() now let us get rid of Result<(), Box<Error + Send + Sync>> and we can use ChainResult<(), Func1ErrorKind>.

In main we can now directly use the methods of ChainError<T> without downcasting the error first.

Also a nice match on ChainError<T>.kind() is now possible, which returns &T, meaning &Func1ErrorKind here.

use chainerror::*;
use std::error::Error;
use std::io;
use std::result::Result;

fn do_some_io() -> Result<(), Box<Error + Send + Sync>> {
    Err(io::Error::from(io::ErrorKind::NotFound))?;
    Ok(())
}

derive_str_cherr!(Func2Error);

fn func2() -> Result<(), Box<Error + Send + Sync>> {
    let filename = "foo.txt";
    do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?;
    Ok(())
}

#[derive(Debug)]
enum Func1ErrorKind {
    Func2,
    IO(String),
}

impl ::std::fmt::Display for Func1ErrorKind {
    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
        match self {
            Func1ErrorKind::Func2 => write!(f, "func1 error calling func2"),
            Func1ErrorKind::IO(filename) => write!(f, "Error reading '{}'", filename),
        }
    }
}
impl ::std::error::Error for Func1ErrorKind {}

fn func1() -> ChainResult<(), Func1ErrorKind> {
    func2().map_err(|e| cherr!(e, Func1ErrorKind::Func2))?;
    let filename = String::from("bar.txt");
    do_some_io().map_err(|e| cherr!(e, Func1ErrorKind::IO(filename)))?;
    Ok(())
}

fn main() -> Result<(), Box<Error + Send + Sync>> {
    if let Err(e) = func1() {
        match e.kind() {
            Func1ErrorKind::Func2 => eprintln!("Main Error Report: func1 error calling func2"),
            Func1ErrorKind::IO(filename) => {
                eprintln!("Main Error Report: func1 error reading '{}'", filename)
            }
        }

        if let Some(e) = e.find_chain_cause::<Func2Error>() {
            eprintln!("\nError reported by Func2Error: {}", e)
        }

        eprintln!("\nDebug Error:\n{:?}", e);
    }
    Ok(())
}
#[allow(dead_code)]
mod chainerror {
{{#includecomment ../src/lib.rs}}
}