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
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#![deny(missing_docs)]
#![cfg_attr(test, deny(warnings))]

//! A generic, extendable Error type.

extern crate typeable;
extern crate traitobject;

use std::fmt::Debug;
use std::any::TypeId;
use std::error::Error as StdError;
use std::mem;

use typeable::Typeable;

/// An extension to std::error::Error which provides dynamic downcasting of
/// errors for use in highly generic contexts.
///
/// ## When to use this trait
///
/// In the vast majority of cases, a library-specific `enum` should be used
/// for cases where there can be many different types of errors. This has
/// the benefit of being very performant and benefiting from all sorts
/// of static checking at both the instantiation site and the handling
/// site of the error.
///
/// In other cases, being generic over `std::error::Error` may be correct
/// - usually for logging errors or in other places where an error is
/// used as *input*.
///
/// Now, a motivating example for this trait, which doesn't fall under
/// either of these cases:
///
/// Imagine we are creating a simple web middleware for verifying incoming
/// HTTP requests. It will take in many different user-defined `Verifier`s
/// and will call them one after the other, rejecting the request on any
/// error.
///
/// The first step would be to write a `Verifier` trait:
///
/// ```ignore
/// # struct Request;
/// pub trait Verifier {
///     /// Verify the request, yielding an error if the request is invalid.
///     fn verify(&Request) -> Result<(), ???>;
/// }
/// ```
///
/// A problem quickly arises - what type do we use for the `Err` case? We
/// cannot use a concrete type since each `Verifier` may wish to throw
/// any number of different errors, and we cannot use a generic since
/// the type is chosen by the implementor, not the caller, and it cannot
/// be a generic on the trait since we will want to store many `Verifier`s
/// together.
///
/// Enter: `Box<error::Error>`, a type which can be used to represent
/// any `std::error::Error` with the sufficient bounds, and can *also*
/// be handled later by downcasting it to the right error using either
/// `.downcast` or the `match_error!` macro. This type can be used to meet
/// the needs of consumers like `Verifier`, but should not be used in cases
/// where enums or generics are better suited.
pub trait Error: Debug + Send + Typeable + StdError { }

impl<S: StdError + Debug + Send + Typeable> Error for S { }

impl Error {
    /// Is this `Error` object of type `E`?
    pub fn is<E: Error>(&self) -> bool { self.get_type() == TypeId::of::<E>() }

    /// If this error is `E`, downcast this error to `E`, by reference.
    pub fn downcast<E: Error>(&self) -> Option<&E> {
        if self.is::<E>() {
            unsafe { Some(mem::transmute(traitobject::data(self))) }
        } else {
            None
        }
    }
}

impl Error + Send {
    /// Is this `Error + Send` object of type `E`?
    pub fn is<E: Error + Send>(&self) -> bool { self.get_type() == TypeId::of::<E>() }

    /// If this error is `E`, downcast this error to `E`, by reference.
    pub fn downcast<E: Error + Send>(&self) -> Option<&E> {
        if self.is::<E>() {
            unsafe { Some(mem::transmute(traitobject::data(self))) }
        } else {
            None
        }
    }
}

impl<E: Error> From<E> for Box<Error> {
    fn from(e: E) -> Box<Error> { Box::new(e) }
}

#[macro_export]
macro_rules! match_error {
    ($m:expr, $i1:pat => $t1:ty: $e1:expr) => {{
        let tmp = $m;
        match tmp.downcast::<$t1>() {
            Some($i1) => Some($e1),
            None => None,
        }
    }};

    ($m:expr, $i1:pat => $t1:ty: $e1:expr, $($i:pat => $t:ty: $e:expr),+) => {{
        let tmp = $m;
        match tmp.downcast::<$t1>() {
            Some($i1) => Some($e1),
            None => match_error!(tmp, $($i: $t => $e),*),
        }
    }};
}

#[cfg(test)]
mod test {
    use super::Error;
    use std::error::Error as StdError;
    use std::fmt::Error as FmtError;
    use std::fmt::Display;
    use std::fmt::Formatter;

    #[derive(Debug, PartialEq)]
    pub struct ParseError {
        location: usize,
    }

    impl StdError for ParseError {
        fn description(&self) -> &str { "Parse Error" }
    }

    impl Display for ParseError {
        fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
            self.description().fmt(f)
        }
    }

    #[test] fn test_generic() {
        fn produce_parse_error() -> Box<Error> {
            Box::new(ParseError { location: 7 })
        }

        fn generic_handler(raw: Box<Error>) {
            (match_error! { raw,
                parse => ParseError: {
                    assert_eq!(*parse, ParseError { location: 7 })
                }
            }).unwrap()
        }

        generic_handler(produce_parse_error())
    }
}