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("Cluster is busy or unavailable")]
87 ClusterBusy,
88}
89
90impl From<reqwest::Error> for RestError {
91 fn from(err: reqwest::Error) -> Self {
92 RestError::RequestFailed(err.to_string())
93 }
94}
95
96impl From<serde_json::Error> for RestError {
97 fn from(err: serde_json::Error) -> Self {
98 RestError::SerializationError(err.to_string())
99 }
100}
101
102impl RestError {
103 pub fn is_not_found(&self) -> bool {
105 matches!(self, RestError::NotFound)
106 || matches!(self, RestError::ApiError { code, .. } if *code == 404)
107 }
108
109 pub fn is_unauthorized(&self) -> bool {
111 matches!(self, RestError::Unauthorized)
112 || matches!(self, RestError::AuthenticationFailed)
113 || matches!(self, RestError::ApiError { code, .. } if *code == 401)
114 }
115
116 pub fn is_server_error(&self) -> bool {
118 matches!(self, RestError::ServerError(_))
119 || matches!(self, RestError::ApiError { code, .. } if *code >= 500)
120 }
121
122 pub fn is_timeout(&self) -> bool {
124 matches!(self, RestError::Timeout)
125 }
126
127 pub fn is_rate_limited(&self) -> bool {
129 matches!(self, RestError::RateLimited { .. })
130 || matches!(self, RestError::ApiError { code, .. } if *code == 429)
131 }
132
133 pub fn is_conflict(&self) -> bool {
135 matches!(self, RestError::AlreadyExists)
136 || matches!(self, RestError::Conflict(_))
137 || matches!(self, RestError::ApiError { code, .. } if *code == 409)
138 }
139
140 pub fn is_cluster_busy(&self) -> bool {
142 matches!(self, RestError::ClusterBusy)
143 || matches!(self, RestError::ApiError { code, .. } if *code == 503)
144 }
145
146 pub fn is_retryable(&self) -> bool {
148 self.is_timeout()
149 || self.is_rate_limited()
150 || self.is_cluster_busy()
151 || self.is_server_error()
152 }
153
154 pub fn is_bad_request(&self) -> bool {
156 matches!(self, RestError::ValidationError(_))
157 || matches!(self, RestError::ApiError { code, .. } if *code == 400)
158 }
159}
160
161pub type Result<T> = std::result::Result<T, RestError>;