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
use serde::Deserialize;
use std::error::Error as StdError;
use std::fmt;

/// A global error enum that encapsulates other more specific
/// types of errors.
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
    /// Wraps errors from the underlying [reqwest::Client] that cannot be mapped
    /// to a more specific error type.
    Client(reqwest::Error),
    /// 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 [ApiError].
    ApiError(ApiError),
    EmptySeriesSelector,
    UrlParse(url::ParseError),
    ResponseParse(serde_json::Error),
    MissingField(MissingFieldError),
}

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

impl StdError for Error {}

/// 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 ApiError {
    #[serde(alias = "errorType")]
    pub(crate) error_type: ApiErrorType,
    #[serde(alias = "error")]
    pub(crate) message: String,
}

impl fmt::Display for ApiError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(
            f,
            "the API returned an error of type {}: {}",
            self.error_type, self.message
        )
    }
}

impl ApiError {
    /// Returns the parsed version of the error type that was given by the Prometheus API.
    pub fn error_type(&self) -> ApiErrorType {
        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 == ApiErrorType::Timeout
    }

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

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

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

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

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

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

/// The parsed error type as returned by the Prometheus API.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Deserialize, PartialEq)]
pub enum ApiErrorType {
    #[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 ApiErrorType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Self::Timeout => write!(f, "timeout"),
            Self::Canceled => write!(f, "canceled"),
            Self::Execution => write!(f, "execution"),
            Self::BadData => write!(f, "bad_data"),
            Self::Internal => write!(f, "internal"),
            Self::Unavailable => write!(f, "unavailable"),
            Self::NotFound => write!(f, "not_found"),
        }
    }
}

/// This error is thrown when a field is unexpectedly not part of the API response.
#[derive(Debug, Clone, PartialEq)]
pub struct MissingFieldError(pub(crate) &'static str);

impl fmt::Display for MissingFieldError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let MissingFieldError(field) = self;
        write!(
            f,
            "expected field '{}' is missing from the JSON payload",
            field
        )
    }
}