1use reqwest::{StatusCode, header::HeaderMap};
2
3#[derive(Debug)]
5pub struct ApiError {
6 pub status: StatusCode,
8 pub headers: HeaderMap,
10 pub body: String,
12}
13
14impl std::fmt::Display for ApiError {
15 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16 write!(f, "HTTP {}: {}", self.status, self.body)
17 }
18}
19
20#[derive(Debug)]
22#[non_exhaustive]
23pub enum Error {
24 AuthenticationError(Box<ApiError>),
26 PermissionDeniedError(Box<ApiError>),
28 ApiConnectionError(reqwest::Error),
30 InvalidRequestError(Box<ApiError>),
32 RateLimitError(Box<ApiError>),
34 NotFoundError(Box<ApiError>),
36 ListenApiError(Box<ApiError>),
38 InvalidParameter(String),
40 Reqwest(reqwest::Error),
42 Json(serde_json::Error),
44}
45
46impl Error {
47 pub(crate) fn from_api(error: Box<ApiError>) -> Self {
48 match error.status {
49 StatusCode::BAD_REQUEST => Self::InvalidRequestError(error),
50 StatusCode::UNAUTHORIZED => Self::AuthenticationError(error),
51 StatusCode::FORBIDDEN => Self::PermissionDeniedError(error),
52 StatusCode::NOT_FOUND => Self::NotFoundError(error),
53 StatusCode::TOO_MANY_REQUESTS => Self::RateLimitError(error),
54 _ => Self::ListenApiError(error),
55 }
56 }
57
58 pub fn api_error(&self) -> Option<&ApiError> {
60 match self {
61 Self::AuthenticationError(e)
62 | Self::PermissionDeniedError(e)
63 | Self::InvalidRequestError(e)
64 | Self::RateLimitError(e)
65 | Self::NotFoundError(e)
66 | Self::ListenApiError(e) => Some(e),
67 _ => None,
68 }
69 }
70}
71
72impl From<reqwest::Error> for Error {
73 fn from(error: reqwest::Error) -> Self {
74 if error.is_connect() || error.is_timeout() {
75 Self::ApiConnectionError(error)
76 } else {
77 Self::Reqwest(error)
78 }
79 }
80}
81
82impl From<serde_json::Error> for Error {
83 fn from(error: serde_json::Error) -> Self {
84 Self::Json(error)
85 }
86}
87
88impl std::error::Error for Error {
89 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
90 match self {
91 Self::Reqwest(e) | Self::ApiConnectionError(e) => Some(e),
92 Self::Json(e) => Some(e),
93 _ => None,
94 }
95 }
96}
97
98impl std::fmt::Display for Error {
99 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
100 if let Some(error) = self.api_error() {
101 return std::fmt::Display::fmt(error, f);
102 }
103 match self {
104 Self::Reqwest(e) | Self::ApiConnectionError(e) => std::fmt::Display::fmt(e, f),
105 Self::Json(e) => std::fmt::Display::fmt(e, f),
106 Self::InvalidParameter(message) => f.write_str(message),
107 _ => unreachable!("HTTP error handled above"),
108 }
109 }
110}