1use std::time::Duration;
26use thiserror::Error;
27
28#[derive(Error, Debug)]
30pub enum CoreError {
31 #[error("Cloud API error: {0}")]
33 Cloud(#[from] redis_cloud::CloudError),
34
35 #[error("Enterprise API error: {0}")]
37 Enterprise(#[from] redis_enterprise::RestError),
38
39 #[error("Task timed out after {0:?}")]
41 TaskTimeout(Duration),
42
43 #[error("Task failed: {0}")]
45 TaskFailed(String),
46
47 #[error("Validation error: {0}")]
49 Validation(String),
50
51 #[error("Configuration error: {0}")]
53 Config(String),
54}
55
56pub type Result<T> = std::result::Result<T, CoreError>;
58
59impl CoreError {
60 #[must_use]
62 pub fn is_not_found(&self) -> bool {
63 match self {
64 CoreError::Cloud(e) => e.is_not_found(),
65 CoreError::Enterprise(e) => e.is_not_found(),
66 _ => false,
67 }
68 }
69
70 #[must_use]
72 pub fn is_unauthorized(&self) -> bool {
73 match self {
74 CoreError::Cloud(e) => e.is_unauthorized(),
75 CoreError::Enterprise(e) => e.is_unauthorized(),
76 _ => false,
77 }
78 }
79
80 #[must_use]
82 pub fn is_server_error(&self) -> bool {
83 match self {
84 CoreError::Cloud(e) => e.is_server_error(),
85 CoreError::Enterprise(e) => e.is_server_error(),
86 _ => false,
87 }
88 }
89
90 #[must_use]
92 pub fn is_timeout(&self) -> bool {
93 match self {
94 CoreError::Cloud(e) => e.is_timeout(),
95 CoreError::Enterprise(e) => e.is_timeout(),
96 CoreError::TaskTimeout(_) => true,
97 _ => false,
98 }
99 }
100
101 #[must_use]
103 pub fn is_rate_limited(&self) -> bool {
104 match self {
105 CoreError::Cloud(e) => e.is_rate_limited(),
106 CoreError::Enterprise(e) => e.is_rate_limited(),
107 _ => false,
108 }
109 }
110
111 #[must_use]
113 pub fn is_conflict(&self) -> bool {
114 match self {
115 CoreError::Cloud(e) => e.is_conflict(),
116 CoreError::Enterprise(e) => e.is_conflict(),
117 _ => false,
118 }
119 }
120
121 #[must_use]
123 pub fn is_bad_request(&self) -> bool {
124 match self {
125 CoreError::Cloud(e) => e.is_bad_request(),
126 CoreError::Enterprise(e) => e.is_bad_request(),
127 CoreError::Validation(_) => true,
128 _ => false,
129 }
130 }
131
132 #[must_use]
134 pub fn is_retryable(&self) -> bool {
135 match self {
136 CoreError::Cloud(e) => e.is_retryable(),
137 CoreError::Enterprise(e) => e.is_retryable(),
138 CoreError::TaskTimeout(_) => true, _ => false,
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147 use redis_cloud::CloudError;
148 use redis_enterprise::RestError;
149
150 #[test]
151 fn test_core_error_from_cloud() {
152 let cloud_err = CloudError::NotFound {
153 message: "Database not found".to_string(),
154 };
155 let core_err: CoreError = cloud_err.into();
156
157 assert!(core_err.is_not_found());
158 assert!(!core_err.is_unauthorized());
159 assert!(!core_err.is_retryable());
160 }
161
162 #[test]
163 fn test_core_error_from_enterprise() {
164 let enterprise_err = RestError::NotFound;
165 let core_err: CoreError = enterprise_err.into();
166
167 assert!(core_err.is_not_found());
168 assert!(!core_err.is_unauthorized());
169 }
170
171 #[test]
172 fn test_core_error_cloud_helpers_delegate() {
173 let unauthorized = CloudError::AuthenticationFailed {
175 message: "Bad creds".to_string(),
176 };
177 let core_err: CoreError = unauthorized.into();
178 assert!(core_err.is_unauthorized());
179
180 let rate_limited = CloudError::RateLimited {
181 message: "Too many requests".to_string(),
182 };
183 let core_err: CoreError = rate_limited.into();
184 assert!(core_err.is_rate_limited());
185 assert!(core_err.is_retryable());
186
187 let bad_request = CloudError::BadRequest {
188 message: "Invalid input".to_string(),
189 };
190 let core_err: CoreError = bad_request.into();
191 assert!(core_err.is_bad_request());
192 }
193
194 #[test]
195 fn test_core_error_enterprise_helpers_delegate() {
196 let unauthorized = RestError::AuthenticationFailed;
198 let core_err: CoreError = unauthorized.into();
199 assert!(core_err.is_unauthorized());
200
201 let server_error = RestError::ServerError("Internal error".to_string());
202 let core_err: CoreError = server_error.into();
203 assert!(core_err.is_server_error());
204 assert!(core_err.is_retryable());
205 }
206
207 #[test]
208 fn test_core_error_task_timeout() {
209 let err = CoreError::TaskTimeout(Duration::from_secs(600));
210 assert!(err.is_timeout());
211 assert!(err.is_retryable()); assert!(!err.is_not_found());
213 }
214
215 #[test]
216 fn test_core_error_validation() {
217 let err = CoreError::Validation("Invalid module name".to_string());
218 assert!(err.is_bad_request()); assert!(!err.is_retryable());
220 }
221
222 #[test]
223 fn test_core_error_display() {
224 let cloud_err: CoreError = CloudError::NotFound {
225 message: "Not found".to_string(),
226 }
227 .into();
228 assert!(cloud_err.to_string().contains("Cloud API error"));
229
230 let timeout_err = CoreError::TaskTimeout(Duration::from_secs(60));
231 assert!(timeout_err.to_string().contains("timed out"));
232 }
233}