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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
//! All error types that are returned by methods in this crate.
use serde::Deserialize;
use std::error::Error as StdError;
use std::fmt;

/// A global error enum that contains all errors that are returned by this
/// library. Some errors are wrappers for errors from underlying libraries.
/// All errors (this enum as well as all contained structs) implement [`std::error::Error`].
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
    /// Wraps errors from the underlying [`reqwest::Client`] that cannot be mapped
    /// to a more specific error type. Deserialization errors also fall into this
    /// category.
    Client(ClientError),
    /// Occurs when Prometheus responds with e.g. HTTP 4xx (e.g. due to a syntax error in a PromQL query).<br>
    /// Details on the error as reported by Prometheus are included in [`PrometheusError`].
    Prometheus(PrometheusError),
    /// Occurs when the [`Client::series`](crate::Client::series) method is called with an empty set of
    /// series [`Selector`](crate::selector::Selector)s. According to the Prometheus API description at least one
    /// [`Selector`](crate::selector::Selector) must be provided.
    EmptySeriesSelector,
    /// Wraps errors from the [`url`] crate.
    ParseUrl(ParseUrlError),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Client(e) => e.fmt(f),
            Self::Prometheus(e) => e.fmt(f),
            Self::EmptySeriesSelector => f.write_str("at least one series selector must be provided in order to query the series endpoint"),
            Self::ParseUrl(e) => e.fmt(f),
        }
    }
}

impl StdError for Error {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        match self {
            Self::Client(e) => e.source(),
            Self::Prometheus(_) => None,
            Self::EmptySeriesSelector => None,
            Self::ParseUrl(e) => e.source(),
        }
    }
}

/// This error is thrown when the JSON response's `status` field contains `error`.<br>
/// The error-related information from the JSON body is included in this error.
#[derive(Debug, Clone, PartialEq, Deserialize)]
pub struct PrometheusError {
    #[serde(alias = "errorType")]
    pub(crate) error_type: PrometheusErrorType,
    #[serde(alias = "error")]
    pub(crate) message: String,
}

impl StdError for PrometheusError {}

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

impl PrometheusError {
    /// Returns the parsed version of the error type that was given by the Prometheus API.
    pub fn error_type(&self) -> PrometheusErrorType {
        self.error_type
    }

    /// Returns the error message that was given by the Prometheus API.
    pub fn message(&self) -> &str {
        &self.message
    }

    pub fn is_timeout(&self) -> bool {
        self.error_type == PrometheusErrorType::Timeout
    }

    pub fn is_canceled(&self) -> bool {
        self.error_type == PrometheusErrorType::Canceled
    }

    pub fn is_execution(&self) -> bool {
        self.error_type == PrometheusErrorType::Execution
    }

    pub fn is_bad_data(&self) -> bool {
        self.error_type == PrometheusErrorType::BadData
    }

    pub fn is_internal(&self) -> bool {
        self.error_type == PrometheusErrorType::Internal
    }

    pub fn is_unavailable(&self) -> bool {
        self.error_type == PrometheusErrorType::Unavailable
    }

    pub fn is_not_found(&self) -> bool {
        self.error_type == PrometheusErrorType::NotFound
    }
}

/// The parsed error type as returned by the Prometheus API.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
pub enum PrometheusErrorType {
    #[serde(alias = "timeout")]
    Timeout,
    #[serde(alias = "canceled")]
    Canceled,
    #[serde(alias = "execution")]
    Execution,
    #[serde(alias = "bad_data")]
    BadData,
    #[serde(alias = "internal")]
    Internal,
    #[serde(alias = "unavailable")]
    Unavailable,
    #[serde(alias = "not_found")]
    NotFound,
}

impl fmt::Display for PrometheusErrorType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Timeout => f.write_str("timeout"),
            Self::Canceled => f.write_str("canceled"),
            Self::Execution => f.write_str("execution"),
            Self::BadData => f.write_str("bad_data"),
            Self::Internal => f.write_str("internal"),
            Self::Unavailable => f.write_str("unavailable"),
            Self::NotFound => f.write_str("not_found"),
        }
    }
}

/// Is thrown when the [`Client`](crate::Client) or the underlying
/// [`reqwest::Error`] fail to build or execute a request.
#[derive(Debug)]
pub struct ClientError {
    pub(crate) message: &'static str,
    pub(crate) source: Option<reqwest::Error>,
}

impl fmt::Display for ClientError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.message)
    }
}

impl StdError for ClientError {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        self.source.as_ref().map(|e| e as &dyn StdError)
    }
}

impl ClientError {
    /// Obtain the [`reqwest::Error`] that is the actual cause of this
    /// error or `None` if the error originated in [`Client`](crate::Client)
    /// itself.<br>
    pub fn inner(&self) -> Option<&reqwest::Error> {
        self.source.as_ref()
    }
}

/// Is thrown when the URL that is used to instantiate the [`Client`](crate::Client)
/// is invalid.
#[derive(Debug)]
pub struct ParseUrlError {
    pub(crate) message: &'static str,
    pub(crate) source: url::ParseError,
}

impl fmt::Display for ParseUrlError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(&self.message)
    }
}

impl StdError for ParseUrlError {
    fn source(&self) -> Option<&(dyn StdError + 'static)> {
        Some(&self.source)
    }
}

impl ParseUrlError {
    /// Obtain the [`url::ParseError`] that is the actual cause of this error.
    pub fn inner(&self) -> &url::ParseError {
        &self.source
    }
}