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
157
158
//! Contains etcd error types.

use std::convert::From;
use std::error::Error as StdError;
use std::fmt::{Display, Error as FmtError, Formatter};

use hyper::Error as HttpError;
use hyper::error::UriError;
#[cfg(feature = "tls")]
use native_tls::Error as TlsError;
use serde_json::Error as SerializationError;
use tokio_timer::TimeoutError as TokioTimeoutError;
use url::ParseError as UrlError;

/// An error returned by an etcd API endpoint.
///
/// This is a logical error, as opposed to other types of errors that may occur when using this
/// crate, such as network or serialization errors. See `Error` for the other types of errors.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq)]
pub struct ApiError {
    /// The key that was being operated upon or reason for the failure.
    pub cause: Option<String>,
    /// The etcd error code.
    #[serde(rename = "errorCode")]
    pub error_code: u64,
    /// The etcd index.
    pub index: u64,
    /// A human-friendly description of the error.
    pub message: String,
}

impl Display for ApiError {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
        write!(f, "{}", self.message)
    }
}

impl StdError for ApiError {
    fn description(&self) -> &str {
        &self.message
    }
}

/// An error returned when an operation fails for some reaosn.
#[derive(Debug)]
pub enum Error {
    /// An error returned by an etcd API endpoint.
    Api(ApiError),
    /// An error at the HTTP protocol layer.
    Http(HttpError),
    /// An error returned when invalid conditions have been provided for a compare-and-delete or
    /// compare-and-swap operation.
    InvalidConditions,
    /// An error returned when an etcd cluster member's endpoint is not a valid URI.
    InvalidUri(UriError),
    /// An error returned when the URL for a specific API endpoint cannot be generated.
    InvalidUrl(UrlError),
    /// An error returned when attempting to create a client without at least one member endpoint.
    NoEndpoints,
    /// An error returned when attempting to deserializing invalid JSON.
    Serialization(SerializationError),
    /// An error returned when configuring TLS.
    #[cfg(feature = "tls")]
    Tls(TlsError),
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
        match *self {
            Error::Api(ref error) => write!(f, "{}", error),
            Error::Http(ref error) => write!(f, "{}", error),
            ref error @ Error::InvalidConditions => write!(f, "{}", error.description()),
            Error::InvalidUri(ref error) => write!(f, "{}", error),
            Error::InvalidUrl(ref error) => write!(f, "{}", error),
            ref error @ Error::NoEndpoints => write!(f, "{}", error.description()),
            #[cfg(feature = "tls")]
            Error::Tls(ref error) => write!(f, "{}", error),
            Error::Serialization(ref error) => write!(f, "{}", error),
        }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Api(_) => "the etcd server returned an error",
            Error::Http(_) => "an error occurred during the HTTP request",
            Error::InvalidConditions => "current value or modified index is required",
            Error::InvalidUri(_) => "a supplied endpoint could not be parsed as a URI",
            Error::InvalidUrl(_) => "a URL for the request could not be generated",
            Error::NoEndpoints => "at least one endpoint is required to create a Client",
            #[cfg(feature = "tls")]
            Error::Tls(_) => "an error occurred configuring TLS",
            Error::Serialization(_) => "an error occurred deserializing JSON",
        }
    }
}

impl From<HttpError> for Error {
    fn from(error: HttpError) -> Error {
        Error::Http(error)
    }
}

#[cfg(feature = "tls")]
impl From<TlsError> for Error {
    fn from(error: TlsError) -> Error {
        Error::Tls(error)
    }
}

impl From<UrlError> for Error {
    fn from(error: UrlError) -> Error {
        Error::InvalidUrl(error)
    }
}

impl From<SerializationError> for Error {
    fn from(error: SerializationError) -> Error {
        Error::Serialization(error)
    }
}

impl From<UriError> for Error {
    fn from(error: UriError) -> Error {
        Error::InvalidUri(error)
    }
}

/// An error returned by `kv::watch`.
#[derive(Debug)]
pub enum WatchError {
    /// An error for each failed request to an etcd member.
    Other(Vec<Error>),
    /// The supplied timeout was reached before any request successfully completed.
    Timeout,
}

impl<T> From<TokioTimeoutError<T>> for WatchError {
    fn from(_: TokioTimeoutError<T>) -> Self {
        WatchError::Timeout
    }
}

impl Display for WatchError {
    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
        match *self {
            WatchError::Timeout => write!(f, "{}", self.description()),
            ref other => other.fmt(f),
        }
    }
}

impl StdError for WatchError {
    fn description(&self) -> &str {
        "operation timed out"
    }
}