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
use std::io;
use ws;
use ring;
#[cfg(feature = "media")]
use reqwest;
use json;
use base64;
use protobuf;

macro_rules! impl_from_for_error {
        ($error:ident, $($var:ident => $orig:ty),*) => {
                $(
                        impl From<$orig> for $error {
                                fn from(err: $orig) -> $error {
                                        $error::$var(err)
                                }
                        }
                 )*
        }
}

#[macro_export]
macro_rules! bail_untyped {
        ($msg:expr) => {
                return Err(WaError::Untyped($msg.into()));
        };
        ($($arg:tt)*) => {
                return Err(WaError::UntypedOwned(format!($($arg)*)));
        }
}

pub trait WaErrorContext {
        fn with_context(self, ctx: &'static str) -> Self;
        fn with_owned_context<T: Into<String>>(self, ctx: T) -> Self;
}
impl<T> WaErrorContext for Result<T> {
        fn with_context(self, ctx: &'static str) -> Self {
                self.map_err(|e| {
                        WaError::Context(ctx, Box::new(e))
                })
        }
        fn with_owned_context<U: Into<String>>(self, ctx: U) -> Self {
                self.map_err(|e| {
                        WaError::OwnedContext(ctx.into(), Box::new(e))
                })
        }
}
#[derive(Debug, Fail)]
pub enum WaError {
        #[fail(display = "I/O error: {}", _0)]
        Io(io::Error),
        #[fail(display = "WebSocket error: {}", _0)]
        Websocket(ws::Error),
        #[fail(display = "Crypto error: {}", _0)]
        Crypto(ring::error::Unspecified),
        #[cfg(feature = "media")]
        #[fail(display = "reqwest error: {}", _0)]
        Reqwest(reqwest::Error),
        #[fail(display = "JSON error: {}", _0)]
        Json(json::Error),
        #[fail(display = "base64 decode error: {}", _0)]
        Base64(base64::DecodeError),
        #[fail(display = "Protobuf error: {}", _0)]
        Protobuf(protobuf::ProtobufError),
        #[fail(display = "Missing node attribute \"{}\"", _0)]
        NodeAttributeMissing(&'static str),
        #[fail(display = "Missing JSON field \"{}\"", _0)]
        JsonFieldMissing(&'static str),
        #[fail(display = "while {}: {}", _0, _1)]
        Context(&'static str, Box<WaError>),
        #[fail(display = "while {}: {}", _0, _1)]
        OwnedContext(String, Box<WaError>),
        #[fail(display = "unknown tag {}", _0)]
        InvalidTag(u8),
        #[fail(display = "{}", _0)]
        UntypedOwned(String),
        #[fail(display = "{}", _0)]
        Untyped(&'static str)
}

pub type WaResult<T> = ::std::result::Result<T, WaError>;
// FIXME: to avoid changing all the damn result types everywhere
pub(crate) type Result<T> = WaResult<T>;

impl_from_for_error!(WaError,
                     Io => io::Error,
                     Websocket => ws::Error,
                     Crypto => ring::error::Unspecified,
                     Json => json::Error,
                     Base64 => base64::DecodeError,
                     Protobuf => protobuf::ProtobufError,
                     UntypedOwned => String,
                     Untyped => &'static str);
#[cfg(feature = "media")]
impl_from_for_error!(WaError,
                     Reqwest => reqwest::Error);