Finding an Error cause

To distinguish the errors occuring in various places, we can define named string errors with the "new type" pattern.

derive_str_cherr!(Func2Error);
derive_str_cherr!(Func1Error);

Instead of ChainError<String> we now have struct Func1Error(String) and ChainError<Func1Error>.

In the main function you can see, how we can match the different errors.

Also see:

            if let Some(f2err) = f1err.find_chain_cause::<Func2Error>() {

as a shortcut to

            if let Some(f2err) = f1err.find_cause::<ChainError<Func2Error>>() {

hiding the ChainError<T> implementation detail.

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_str_cherr!(Func1Error);

fn func1() -> Result<(), Box<Error + Send + Sync>> {
    func2().map_err(mstrerr!(Func1Error, "func1 error"))?;
    Ok(())
}

fn main() -> Result<(), Box<Error + Send + Sync>> {
    if let Err(e) = func1() {
        if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() {
            eprintln!("Func1Error: {}", f1err);

            if let Some(f2err) = f1err.find_cause::<ChainError<Func2Error>>() {
                eprintln!("Func2Error: {}", f2err);
            }

            if let Some(f2err) = f1err.find_chain_cause::<Func2Error>() {
                eprintln!("Debug Func2Error:\n{:?}", f2err);
            }
        }
    }
    Ok(())
}
#[allow(dead_code)]
mod chainerror {
{{#includecomment ../src/lib.rs}}
}