Skip to main content

redis_cloud/
error.rs

1//! Error types for Redis Cloud API client
2//!
3//! This module provides error handling for all API operations, including
4//! typed errors for common HTTP status codes and network failures.
5//!
6//! # Error Types
7//!
8//! - `CloudError::BadRequest` - HTTP 400 errors
9//! - `CloudError::AuthenticationFailed` - HTTP 401 errors
10//! - `CloudError::Forbidden` - HTTP 403 errors
11//! - `CloudError::NotFound` - HTTP 404 errors
12//! - `CloudError::RateLimited` - HTTP 429 errors
13//! - `CloudError::InternalServerError` - HTTP 500 errors
14//! - `CloudError::ServiceUnavailable` - HTTP 503 errors
15//!
16//! # Retryable Errors
17//!
18//! Some errors are considered retryable (transient failures that may succeed on retry):
19//! - Rate limited (429)
20//! - Service unavailable (503)
21//! - Connection/request errors (network issues)
22//!
23//! Use `CloudError::is_retryable()` to check if an error should be retried.
24
25use thiserror::Error;
26
27/// Errors that can occur when interacting with the Redis Cloud API
28#[derive(Error, Debug, Clone)]
29pub enum CloudError {
30    /// HTTP request failed (network error, timeout, etc.)
31    #[error("HTTP request failed: {0}")]
32    Request(String),
33
34    /// Bad Request (400) - Invalid request parameters
35    #[error("Bad Request (400): {message}")]
36    BadRequest {
37        /// Error message from the API
38        message: String,
39    },
40
41    /// Authentication failed (401) - Invalid or missing credentials
42    #[error("Authentication failed (401): {message}")]
43    AuthenticationFailed {
44        /// Error message from the API
45        message: String,
46    },
47
48    /// Forbidden (403) - Insufficient permissions
49    #[error("Forbidden (403): {message}")]
50    Forbidden {
51        /// Error message from the API
52        message: String,
53    },
54
55    /// Not Found (404) - Resource does not exist
56    #[error("Not Found (404): {message}")]
57    NotFound {
58        /// Error message from the API
59        message: String,
60    },
61
62    /// Precondition Failed (412) - Feature flag is disabled
63    #[error("Precondition Failed (412): Feature flag for this flow is off")]
64    PreconditionFailed,
65
66    /// Rate Limited (429) - Too many requests
67    #[error("Rate Limited (429): {message}")]
68    RateLimited {
69        /// Error message from the API
70        message: String,
71    },
72
73    /// Internal Server Error (500) - Server-side error
74    #[error("Internal Server Error (500): {message}")]
75    InternalServerError {
76        /// Error message from the API
77        message: String,
78    },
79
80    /// Service Unavailable (503) - Server temporarily unavailable
81    #[error("Service Unavailable (503): {message}")]
82    ServiceUnavailable {
83        /// Error message from the API
84        message: String,
85    },
86
87    /// Generic API error for other HTTP status codes
88    #[error("API error ({code}): {message}")]
89    ApiError {
90        /// HTTP status code
91        code: u16,
92        /// Error message from the API
93        message: String,
94    },
95
96    /// Connection error (failed to establish connection)
97    #[error("Connection error: {0}")]
98    ConnectionError(String),
99
100    /// JSON serialization/deserialization error
101    #[error("JSON error: {0}")]
102    JsonError(String),
103}
104
105impl CloudError {
106    /// Returns true if this error is retryable.
107    ///
108    /// Retryable errors include:
109    /// - Rate limited (429)
110    /// - Service unavailable (503)
111    /// - Connection/request errors (may be transient network issues)
112    ///
113    /// # Examples
114    ///
115    /// ```
116    /// use redis_cloud::CloudError;
117    ///
118    /// let error = CloudError::RateLimited { message: "Too many requests".to_string() };
119    /// assert!(error.is_retryable());
120    ///
121    /// let error = CloudError::NotFound { message: "Resource not found".to_string() };
122    /// assert!(!error.is_retryable());
123    /// ```
124    #[must_use]
125    pub fn is_retryable(&self) -> bool {
126        matches!(
127            self,
128            CloudError::RateLimited { .. }
129                | CloudError::ServiceUnavailable { .. }
130                | CloudError::Request(_)
131                | CloudError::ConnectionError(_)
132        )
133    }
134}
135
136impl From<reqwest::Error> for CloudError {
137    fn from(err: reqwest::Error) -> Self {
138        CloudError::Request(err.to_string())
139    }
140}
141
142impl From<serde_json::Error> for CloudError {
143    fn from(err: serde_json::Error) -> Self {
144        CloudError::JsonError(err.to_string())
145    }
146}
147
148/// Result type alias for Redis Cloud operations
149pub type Result<T> = std::result::Result<T, CloudError>;