1use reqwest::StatusCode;
2use thiserror::Error;
3use url::ParseError;
4
5#[derive(Debug, Error)]
6pub enum ApiClientError {
7 #[error("invalid base URL: {0}")]
8 InvalidUrl(#[from] ParseError),
9
10 #[error("transport error: {0}")]
11 Transport(#[from] reqwest::Error),
12
13 #[error("failed to decode response body: {0}")]
14 Decode(#[from] serde_json::Error),
15
16 #[error("server returned {status}: {message}")]
17 Api { status: StatusCode, message: String },
18}
19
20impl ApiClientError {
21 pub fn status(&self) -> Option<StatusCode> {
22 match self {
23 Self::Api { status, .. } => Some(*status),
24 _ => None,
25 }
26 }
27}
28
29#[cfg(test)]
30mod tests {
31 use super::*;
32
33 #[test]
34 fn status_returns_some_for_api_errors() {
35 let err = ApiClientError::Api {
36 status: StatusCode::BAD_REQUEST,
37 message: "oops".into(),
38 };
39 assert_eq!(err.status(), Some(StatusCode::BAD_REQUEST));
40 }
41
42 #[test]
43 fn status_returns_none_for_other_variants() {
44 let decode_err: ApiClientError = serde_json::from_str::<serde_json::Value>("not-json")
45 .unwrap_err()
46 .into();
47 assert!(matches!(decode_err, ApiClientError::Decode(_)));
48 assert_eq!(decode_err.status(), None);
49 }
50}