redis_enterprise/
error.rs1use std::time::Duration;
4use thiserror::Error;
5
6#[derive(Error, Debug, Clone)]
7pub enum RestError {
8 #[error("Invalid URL: {0}")]
9 InvalidUrl(String),
10
11 #[error("HTTP request failed: {0}")]
12 RequestFailed(String),
13
14 #[error("Authentication failed")]
15 AuthenticationFailed,
16
17 #[error("API error: {message} (code: {code})")]
18 ApiError { code: u16, message: String },
19
20 #[error("Serialization error: {0}")]
21 SerializationError(String),
22
23 #[error("Parse error: {0}")]
24 ParseError(String),
25
26 #[error("Connection error: {0}")]
27 ConnectionError(String),
28
29 #[error("TLS certificate error: {0}")]
30 TlsError(String),
31
32 #[error("Not connected to REST API")]
33 NotConnected,
34
35 #[error("Validation error: {0}")]
36 ValidationError(String),
37
38 #[error("Resource not found")]
39 NotFound,
40
41 #[error("Unauthorized")]
42 Unauthorized,
43
44 #[error("Server error: {0}")]
45 ServerError(String),
46
47 #[error("Request timed out")]
48 Timeout,
49
50 #[error("Rate limited{}", .retry_after.map(|d| format!(" (retry after {:?})", d)).unwrap_or_default())]
51 RateLimited { retry_after: Option<Duration> },
52
53 #[error("Resource already exists")]
54 AlreadyExists,
55
56 #[error("Conflict: {0}")]
57 Conflict(String),
58
59 #[error("Cluster is busy or unavailable")]
60 ClusterBusy,
61}
62
63impl From<reqwest::Error> for RestError {
64 fn from(err: reqwest::Error) -> Self {
65 RestError::RequestFailed(err.to_string())
66 }
67}
68
69impl From<serde_json::Error> for RestError {
70 fn from(err: serde_json::Error) -> Self {
71 RestError::SerializationError(err.to_string())
72 }
73}
74
75impl RestError {
76 pub fn is_not_found(&self) -> bool {
78 matches!(self, RestError::NotFound)
79 || matches!(self, RestError::ApiError { code, .. } if *code == 404)
80 }
81
82 pub fn is_unauthorized(&self) -> bool {
84 matches!(self, RestError::Unauthorized)
85 || matches!(self, RestError::AuthenticationFailed)
86 || matches!(self, RestError::ApiError { code, .. } if *code == 401)
87 }
88
89 pub fn is_server_error(&self) -> bool {
91 matches!(self, RestError::ServerError(_))
92 || matches!(self, RestError::ApiError { code, .. } if *code >= 500)
93 }
94
95 pub fn is_timeout(&self) -> bool {
97 matches!(self, RestError::Timeout)
98 }
99
100 pub fn is_rate_limited(&self) -> bool {
102 matches!(self, RestError::RateLimited { .. })
103 || matches!(self, RestError::ApiError { code, .. } if *code == 429)
104 }
105
106 pub fn is_conflict(&self) -> bool {
108 matches!(self, RestError::AlreadyExists)
109 || matches!(self, RestError::Conflict(_))
110 || matches!(self, RestError::ApiError { code, .. } if *code == 409)
111 }
112
113 pub fn is_cluster_busy(&self) -> bool {
115 matches!(self, RestError::ClusterBusy)
116 || matches!(self, RestError::ApiError { code, .. } if *code == 503)
117 }
118
119 pub fn is_retryable(&self) -> bool {
121 self.is_timeout()
122 || self.is_rate_limited()
123 || self.is_cluster_busy()
124 || self.is_server_error()
125 }
126
127 pub fn is_bad_request(&self) -> bool {
129 matches!(self, RestError::ValidationError(_))
130 || matches!(self, RestError::ApiError { code, .. } if *code == 400)
131 }
132}
133
134pub type Result<T> = std::result::Result<T, RestError>;