9 releases

0.1.24 Sep 18, 2020
0.1.23 Sep 18, 2020

#1905 in Rust patterns

MIT license

22KB
146 lines

dynerr

Some macros to help with dynamic error handling/logging.
The goal of this crate is to unify all error types without compromising type safety.

The main features of this crate are the dynerr! and dynmatch! macros. when used alongside the return type DynResult<T>, they allows you to return multiple error types from a function then easily match for them during your error handling. Using dynerr, theres no need to ever wrap errors.

fn example(x: u32) -> DynResult<u32> {                  //returns a dynamic result
    match x {
        1      => Ok(x),                                //Ok()
        2..=4  => dynerr!(ExampleError1::ThisError(x)), //custom error
        5..=10 => dynerr!(ExampleError2::ThatError(x)), //different custom error
        _      => {
            std::env::current_dir()?;                   //an error not even defined by you!
            Ok(x)
        }
    }
}


DynError is an alias for Box<dyn error::Error>. Any error that implements error::Error can be turned into a DynError.
DynResult<T> is just an alias for Result<T, DynError> so anything that works on a Result<T> will still work on a DynResult<T>.
Dynerr works with any error type from any crate, as long as the type being returned implements std::error::Error then DynResult<T> should be able to handle it.
To directly return a custom error its recommended to use the dynerr! macro instead of Err().
To match against the DynError contained in DynResult<T>, use the dynmatch! macro.
dynmatch! usage looks similar to this:

 let i = match example(9) { //returns dyn error
     Ok(i) => i,
     Err(e) => {
         dynmatch!(e,                                                    //the error to match
             type ExampleError1 {                                        //enum error type
                 arm ExampleError1::ThisError(2) => panic!("it was 2!"), //arm [pattern] => {code}
                 _ => panic!("{}",e)                                     //_ => {code}
             },
             type ExampleError2 {                                        //another enum error type
                 arm ExampleError2::ThatError(8) => panic!("it was 8!"), //more arms to match against
                 arm ExampleError2::ThatError(9) => 9,
                 _ => panic!("{}",e)                                     //a final exhaustive match
             },
             _ => panic!("{}",e)                                         //final exhaustive match if type not found
         )
     }
 };


Aside from its main features, dynerr also has some simple macros to help with lazy logging.
log! will log an event to the supplied file. Defaults to event.log if no log file supplied.
logged_panic! will log an event to file then panic. Defaults to event.log if no log file supplied.
check! will call .unwrap_or_else(|e| logged_panic!(e)) on a result. Defaults to event.log if no log file supplied.
If the supplied file doesn't exist then these macros will attempt to create the file.
To delete a log file use the clean! macro.
These macros all rely on either the log or clean_log functions. these functions are capable of panicking but shouldn't ever need to under normal circumstances.

A complete example:

use dynerr::*;
use std::{fmt, error};

//THIS SECTION IS CREATING THE FIRST CUSTOM ERROR
///a custom error type
#[derive(Debug)]
enum ExampleError1 {
    ThisError(u32),
}
//impl display formatting for error
impl fmt::Display for ExampleError1 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ExampleError1::ThisError(i)      => write!(f, "ExampleError1::ThisError: {}",i),
        }
    }
}
//impl error conversion for error
impl error::Error for ExampleError1 {}

//THIS SECTION IS CREATING THE SECOND CUSTOM ERROR
#[derive(Debug)]
enum ExampleError2 {
    ThatError(u32),
}
impl fmt::Display for ExampleError2 {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ExampleError2::ThatError(i)      => write!(f, "ExampleError2::ThatError: {}",i),
        }
    }
}
impl error::Error for ExampleError2 {}


///THIS SECTION IS USING IT

///shows error handling capabilities using DynError
fn example(x: u32) -> DynResult<u32> {
    match x {
        1      => Ok(x),                                //Ok
        2..=4  => dynerr!(ExampleError1::ThisError(x)), //custom error
        5..=10 => dynerr!(ExampleError2::ThatError(x)), //different custom error
        _      => {
            std::fs::File::open("none")?;               //an error not even defined by you!
            Ok(x)
        }
    }
}

fn main() -> DynResult<()> {
    log!("this is a test", "test.log");
    let _i = match example(2) {
        Ok(i) => i,
        Err(e) => {
            dynmatch!(e,                                                                        //the DynError to be matched
                type ExampleError1 {                                                            //an error type
                    arm ExampleError1::ThisError(2) => logged_panic!("it was 2!"),              //arm [pattern] => {code}
                    _ => panic!("{}",e)                                                         //_ => {code}
                },
                type ExampleError2 {
                    arm ExampleError2::ThatError(8) => logged_panic!("it was 8!", "test.log"),
                    arm ExampleError2::ThatError(9) => 9,
                    _ => panic!("{}",e)
                },
                type std::io::Error {                                                           //an error type not defined by you
                    arm i if i.kind() == std::io::ErrorKind::NotFound => panic!("not found"),   //a match guard included in the match
                    _ => panic!("{}", e)
                },
                _ => panic!("{}",e)                                                             //what to do if error type isn't found
            )
        }
    };
    log!("do logged_panic! if error");
    let _i = check!(example(11));
    let _i = check!(example(9), "test.log");
    Ok(())
}

No runtime deps