redis_enterprise/
error.rs1use std::time::Duration;
4use thiserror::Error;
5
6#[derive(Error, Debug, Clone)]
8pub enum RestError {
9 #[error("Invalid URL: {0}")]
11 InvalidUrl(String),
12
13 #[error("HTTP request failed: {0}")]
15 RequestFailed(String),
16
17 #[error("Authentication failed")]
19 AuthenticationFailed,
20
21 #[error("API error: {message} (code: {code})")]
23 ApiError {
24 code: u16,
26 message: String,
28 },
29
30 #[error("Serialization error: {0}")]
32 SerializationError(String),
33
34 #[error("Parse error: {0}")]
36 ParseError(String),
37
38 #[error("Connection error: {0}")]
40 ConnectionError(String),
41
42 #[error("TLS certificate error: {0}")]
44 TlsError(String),
45
46 #[error("Not connected to REST API")]
48 NotConnected,
49
50 #[error("Validation error: {0}")]
52 ValidationError(String),
53
54 #[error("Resource not found")]
56 NotFound,
57
58 #[error("Unauthorized")]
60 Unauthorized,
61
62 #[error("Server error: {0}")]
64 ServerError(String),
65
66 #[error("Request timed out")]
68 Timeout,
69
70 #[error("Rate limited{}", .retry_after.map(|d| format!(" (retry after {:?})", d)).unwrap_or_default())]
72 RateLimited {
73 retry_after: Option<Duration>,
75 },
76
77 #[error("Resource already exists")]
79 AlreadyExists,
80
81 #[error("Conflict: {0}")]
83 Conflict(String),
84
85 #[error("Unsupported operation: {0}")]
88 UnsupportedOperation(String),
89
90 #[error("Cluster is busy or unavailable")]
92 ClusterBusy,
93}
94
95impl From<reqwest::Error> for RestError {
96 fn from(err: reqwest::Error) -> Self {
97 RestError::RequestFailed(err.to_string())
98 }
99}
100
101impl From<serde_json::Error> for RestError {
102 fn from(err: serde_json::Error) -> Self {
103 RestError::SerializationError(err.to_string())
104 }
105}
106
107impl RestError {
108 pub fn is_not_found(&self) -> bool {
110 matches!(self, RestError::NotFound)
111 || matches!(self, RestError::ApiError { code, .. } if *code == 404)
112 }
113
114 pub fn is_unauthorized(&self) -> bool {
116 matches!(self, RestError::Unauthorized)
117 || matches!(self, RestError::AuthenticationFailed)
118 || matches!(self, RestError::ApiError { code, .. } if *code == 401)
119 }
120
121 pub fn is_server_error(&self) -> bool {
123 matches!(self, RestError::ServerError(_))
124 || matches!(self, RestError::ApiError { code, .. } if *code >= 500)
125 }
126
127 pub fn is_timeout(&self) -> bool {
129 matches!(self, RestError::Timeout)
130 }
131
132 pub fn is_rate_limited(&self) -> bool {
134 matches!(self, RestError::RateLimited { .. })
135 || matches!(self, RestError::ApiError { code, .. } if *code == 429)
136 }
137
138 pub fn is_conflict(&self) -> bool {
140 matches!(self, RestError::AlreadyExists)
141 || matches!(self, RestError::Conflict(_))
142 || matches!(self, RestError::ApiError { code, .. } if *code == 409)
143 }
144
145 pub fn is_cluster_busy(&self) -> bool {
147 matches!(self, RestError::ClusterBusy)
148 || matches!(self, RestError::ApiError { code, .. } if *code == 503)
149 }
150
151 pub fn is_retryable(&self) -> bool {
153 self.is_timeout()
154 || self.is_rate_limited()
155 || self.is_cluster_busy()
156 || self.is_server_error()
157 }
158
159 pub fn is_bad_request(&self) -> bool {
161 matches!(self, RestError::ValidationError(_))
162 || matches!(self, RestError::ApiError { code, .. } if *code == 400)
163 }
164}
165
166pub type Result<T> = std::result::Result<T, RestError>;
168
169pub(crate) fn unsupported_operation<T>(operation: &str) -> Result<T> {
171 Err(RestError::UnsupportedOperation(operation.to_string()))
172}