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
105
106
107
108
109
110
//! Stuff for error-handling.

#[cfg(feature = "backtrace")]
use backtrace::Backtrace;
use std::{
    error::Error,
    fmt::{Debug, Display},
};

/// A convenient alias for `Result`.
pub type Result<T, E = Box<dyn Error + Send + Sync + 'static>> = ::std::result::Result<T, E>;

/// A wrapper for giving `ErrorKind`s causes and backtraces.
#[derive(Debug, Display)]
#[display(fmt = "{}", "kind")]
pub struct GenericError<T: Debug + Display> {
    kind: T,

    cause: Option<Box<dyn Error + 'static>>,

    // TODO: Don't derive Display if there's a backtrace.
    #[cfg(feature = "backtrace")]
    backtrace: Backtrace,
}

impl<T: Debug + Display> GenericError<T> {
    /// Returns the backtrace when the error was created.
    #[cfg(feature = "backtrace")]
    pub fn backtrace(&self) -> &Backtrace {
        &self.backtrace
    }

    /// Returns the kind of the error.
    pub fn kind(&self) -> &T {
        &self.kind
    }

    /// Creates an error with the given kind and cause.
    pub fn with_cause<C>(kind: T, cause: C) -> GenericError<T>
    where
        C: Into<Box<dyn Error + 'static>>,
    {
        GenericError {
            kind,
            cause: Some(cause.into()),
            #[cfg(feature = "backtrace")]
            backtrace: Backtrace::new(),
        }
    }
}

impl<T: Debug + Display> Error for GenericError<T> {
    fn source(&self) -> Option<&(dyn Error + 'static)> {
        self.cause.as_ref().map(|boxed| &**boxed)
    }
}

impl<T: Debug + Display> From<T> for GenericError<T> {
    fn from(kind: T) -> GenericError<T> {
        GenericError {
            kind,
            cause: None,
            #[cfg(feature = "backtrace")]
            backtrace: Backtrace::new(),
        }
    }
}

/// An iterator over the causes of an error.
#[derive(Debug)]
pub struct ErrorCauseIter<'a>(Option<&'a (dyn Error + 'static)>);

impl<'a> From<&'a (dyn Error + 'static)> for ErrorCauseIter<'a> {
    fn from(err: &'a (dyn Error + 'static)) -> ErrorCauseIter<'a> {
        ErrorCauseIter(Some(err))
    }
}

impl<'a> Iterator for ErrorCauseIter<'a> {
    type Item = &'a dyn Error;

    fn next(&mut self) -> Option<&'a dyn Error> {
        let err = self.0?;
        self.0 = err.source();
        Some(err)
    }
}

/// Logs an error, including its causes.
#[cfg(feature = "log")]
pub fn log_err(err: &(dyn Error + 'static)) {
    use log::error;

    // Count the number of errors.
    let num_errs = ErrorCauseIter::from(err).count();

    if num_errs <= 1 {
        error!("{}", err);
    } else {
        let mut first = true;
        for err in ErrorCauseIter::from(err) {
            if first {
                first = false;
                error!("           {}", err);
            } else {
                error!("caused by: {}", err);
            }
        }
    }
}