1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
/// A simple yet flexible crate for error management
/// 
/// a lot of these ideas are stolen from SNAFU
/// but i wanted to make a more flexible version that works with all kinds of errors

#[cfg(feature = "print-error")]
extern crate termcolor;

#[cfg(feature = "print-error")]
use std::io;
use std::fmt;
use std::error::Error;
#[cfg(feature = "print-error")]
use std::io::Write;
#[cfg(feature = "print-error")]
use termcolor::{WriteColor, StandardStream, ColorSpec, Color};

#[cfg(feature = "print-error")]
/// A relatively simple utility trait for printing more complicated errors.
pub trait PrintError {
    fn print(&self, io: &mut StandardStream) -> io::Result<()>;
}

#[cfg(feature = "print-error")]
impl<E: std::error::Error + ?Sized> PrintError for E {
    fn print(&self, io: &mut StandardStream) -> io::Result<()> {
        if let Some(cause) = self.source() {
            cause.print(io)?;
        }

        io.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
        write!(io, "error: ")?;

        io.set_color(ColorSpec::new().set_fg(None))?;
        writeln!(io, "{}", self)?;
        
        Ok(())
    }
}

/// A compabitility trait that implements std::error::Error but only when Self implements Display + Debug
pub trait ErrorCompat {
    fn error_source(&self) -> Option<&(dyn Error + 'static)>;
}

trait CompatDisplayDebug: fmt::Display + fmt::Debug + ErrorCompat { }
impl<T> CompatDisplayDebug for T where T: fmt::Display + fmt::Debug + ErrorCompat { }

impl Error for dyn CompatDisplayDebug {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.error_source()
    }
}

/// A trait that takes a context + self to produce a target.
/// This is useful for errors that depend on a context, or when used to generate errors from a context and error (see: [`ErrorContext`]).
/// 
/// [`ErrorContext`]: ../trait.ErrorContext.html
pub trait IntoErrorContext<Context, Target> {
    fn into_target(self, ctx: Context) -> Target;
}

/// Reverse implemented for Result<T, E> where E: [`IntoErrorContext`]
/// 
/// [`IntoErrorContext`]: ../trait.IntoErrorContext.html
pub trait ErrorContext<Context, Target> {
    fn context(self, ctx: Context) -> Target;
    fn context_with<F: Fn() -> Context>(self, ctx: F) -> Target;
}

//impl errorcontext<io::Error, Error> for ErrorKind
//produces errorcontext<ErrorKind, Error> for io::Error
impl<Context, Target, T, E: IntoErrorContext<Context, Target>> ErrorContext<E, Result<T, Target>> for Result<T, Context>  {
    fn context(self, ctx: E) -> Result<T, Target> {
        self.map_err(|x| ctx.into_target(x))
    }

    fn context_with<F: Fn() -> E>(self, ctx: F) -> Result<T, Target> {
        self.map_err(|x| ctx().into_target(x))
    }
}

//Generate a main function for a function that returns a result. Good for quick setups/examples
#[macro_export]
macro_rules! main_res {
    ($main: ident) => {
        fn main() {
            if let Err(err) = $main() {
                println!("{}", err);
            }
        }
    };
}

//Generate a Result type (conviently named "Res") for your error
#[macro_export]
macro_rules! res {
    ($err: ident) => {
        type Res<T> = Result<T, $err>;
    };
    ($err: ident < $($arg: tt),* >) => {
        type Res<$($arg),*, T> = Result<T, $err<$($arg),*>>;
    };
}