dioxus_provider/
errors.rs

1//! # Structured Error Types
2//!
3//! This module provides structured error types for common scenarios in data fetching
4//! and provider operations. Using structured errors instead of generic `String` errors
5//! provides better error handling, debugging, and type safety.
6//!
7//! ## Examples
8//!
9//! ### Using ProviderError for general provider failures:
10//! ```rust
11//! use dioxus_provider::errors::ProviderError;
12//!
13//! #[provider]
14//! async fn fetch_user(user_id: u32) -> Result<User, ProviderError> {
15//!     if user_id == 0 {
16//!         return Err(ProviderError::InvalidInput("User ID cannot be zero".to_string()));
17//!     }
18//!     
19//!     let response = api_call(user_id).await
20//!         .map_err(|e| ProviderError::ExternalService {
21//!             service: "UserAPI".to_string(),
22//!             error: e.to_string(),
23//!         })?;
24//!         
25//!     Ok(response)
26//! }
27//! ```
28//!
29//! ### Using custom domain-specific errors:
30//! ```rust
31//! use dioxus_provider::errors::ProviderError;
32//! use thiserror::Error;
33//!
34//! #[derive(Error, Debug, Clone, PartialEq)]
35//! pub enum UserError {
36//!     #[error("User not found: {id}")]
37//!     NotFound { id: u32 },
38//!     #[error("User is suspended: {reason}")]
39//!     Suspended { reason: String },
40//!     #[error("Permission denied for user {user_id}")]
41//!     PermissionDenied { user_id: u32 },
42//!     #[error("Provider error: {0}")]
43//!     Provider(#[from] ProviderError),
44//! }
45//!
46//! #[provider]
47//! async fn fetch_user_profile(user_id: u32) -> Result<UserProfile, UserError> {
48//!     // Implementation
49//! }
50//! ```
51
52use thiserror::Error;
53
54/// Common error types for provider operations
55#[derive(Error, Debug, Clone, PartialEq)]
56pub enum ProviderError {
57    /// Invalid input parameters
58    #[error("Invalid input: {0}")]
59    InvalidInput(String),
60
61    /// Network or HTTP errors
62    #[error("Network error: {0}")]
63    Network(String),
64
65    /// External service errors  
66    #[error("External service '{service}' error: {error}")]
67    ExternalService { service: String, error: String },
68
69    /// Data parsing or serialization errors
70    #[error("Data parsing error: {0}")]
71    DataParsing(String),
72
73    /// Authentication errors
74    #[error("Authentication failed: {0}")]
75    Authentication(String),
76
77    /// Authorization errors
78    #[error("Authorization failed: {0}")]
79    Authorization(String),
80
81    /// Rate limiting errors
82    #[error("Rate limit exceeded: {0}")]
83    RateLimit(String),
84
85    /// Timeout errors
86    #[error("Operation timed out: {0}")]
87    Timeout(String),
88
89    /// Configuration errors
90    #[error("Configuration error: {0}")]
91    Configuration(String),
92
93    /// Dependency injection errors
94    #[error("Dependency injection failed: {0}")]
95    DependencyInjection(String),
96
97    /// Cache errors
98    #[error("Cache error: {0}")]
99    Cache(String),
100
101    /// Generic provider errors for cases not covered above
102    #[error("Provider error: {0}")]
103    Generic(String),
104}
105
106/// Errors specific to user operations
107#[derive(Error, Debug, Clone, PartialEq)]
108pub enum UserError {
109    /// User not found
110    #[error("User not found: {id}")]
111    NotFound { id: u32 },
112
113    /// User account is suspended
114    #[error("User suspended: {reason}")]
115    Suspended { reason: String },
116
117    /// User account is deleted
118    #[error("User deleted: {id}")]
119    Deleted { id: u32 },
120
121    /// Permission denied for user operation
122    #[error("Permission denied for user {user_id}: {action}")]
123    PermissionDenied { user_id: u32, action: String },
124
125    /// User validation errors
126    #[error("User validation failed: {field}: {reason}")]
127    ValidationFailed { field: String, reason: String },
128
129    /// Wraps provider errors
130    #[error("Provider error: {0}")]
131    Provider(#[from] ProviderError),
132}
133
134/// Errors specific to API operations
135#[derive(Error, Debug, Clone, PartialEq)]
136pub enum ApiError {
137    /// HTTP status errors
138    #[error("HTTP {status}: {message}")]
139    HttpStatus { status: u16, message: String },
140
141    /// JSON parsing errors
142    #[error("JSON parsing failed: {0}")]
143    JsonParsing(String),
144
145    /// Request building errors
146    #[error("Request building failed: {0}")]
147    RequestBuilding(String),
148
149    /// Response processing errors
150    #[error("Response processing failed: {0}")]
151    ResponseProcessing(String),
152
153    /// API endpoint not found
154    #[error("API endpoint not found: {endpoint}")]
155    EndpointNotFound { endpoint: String },
156
157    /// API version mismatch
158    #[error("API version mismatch: expected {expected}, got {actual}")]
159    VersionMismatch { expected: String, actual: String },
160
161    /// Wraps provider errors
162    #[error("Provider error: {0}")]
163    Provider(#[from] ProviderError),
164}
165
166/// Errors specific to database operations
167#[derive(Error, Debug, Clone, PartialEq)]
168pub enum DatabaseError {
169    /// Connection errors
170    #[error("Database connection failed: {0}")]
171    Connection(String),
172
173    /// Query execution errors
174    #[error("Query execution failed: {query}: {error}")]
175    QueryExecution { query: String, error: String },
176
177    /// Transaction errors
178    #[error("Transaction failed: {0}")]
179    Transaction(String),
180
181    /// Migration errors
182    #[error("Database migration failed: {0}")]
183    Migration(String),
184
185    /// Constraint violation errors
186    #[error("Database constraint violation: {constraint}: {details}")]
187    ConstraintViolation { constraint: String, details: String },
188
189    /// Record not found
190    #[error("Record not found: {table}: {id}")]
191    RecordNotFound { table: String, id: String },
192
193    /// Wraps provider errors
194    #[error("Provider error: {0}")]
195    Provider(#[from] ProviderError),
196}
197
198/// Convenience type alias for Results with ProviderError
199pub type ProviderResult<T> = Result<T, ProviderError>;
200
201/// Convenience type alias for Results with UserError
202pub type UserResult<T> = Result<T, UserError>;
203
204/// Convenience type alias for Results with ApiError
205pub type ApiResult<T> = Result<T, ApiError>;
206
207/// Convenience type alias for Results with DatabaseError
208pub type DatabaseResult<T> = Result<T, DatabaseError>;
209
210impl From<String> for ProviderError {
211    fn from(error: String) -> Self {
212        ProviderError::Generic(error)
213    }
214}
215
216impl From<&str> for ProviderError {
217    fn from(error: &str) -> Self {
218        ProviderError::Generic(error.to_string())
219    }
220}
221
222impl From<ProviderError> for String {
223    fn from(error: ProviderError) -> Self {
224        error.to_string()
225    }
226}
227
228#[cfg(test)]
229mod tests {
230    use super::*;
231
232    #[test]
233    fn test_provider_error_display() {
234        let error = ProviderError::InvalidInput("test input".to_string());
235        assert_eq!(error.to_string(), "Invalid input: test input");
236    }
237
238    #[test]
239    fn test_user_error_with_provider_error() {
240        let provider_error = ProviderError::Network("connection failed".to_string());
241        let user_error = UserError::Provider(provider_error);
242        assert_eq!(
243            user_error.to_string(),
244            "Provider error: Network error: connection failed"
245        );
246    }
247
248    #[test]
249    fn test_api_error_http_status() {
250        let error = ApiError::HttpStatus {
251            status: 404,
252            message: "Not Found".to_string(),
253        };
254        assert_eq!(error.to_string(), "HTTP 404: Not Found");
255    }
256
257    #[test]
258    fn test_database_error_constraint_violation() {
259        let error = DatabaseError::ConstraintViolation {
260            constraint: "unique_email".to_string(),
261            details: "Email already exists".to_string(),
262        };
263        assert_eq!(
264            error.to_string(),
265            "Database constraint violation: unique_email: Email already exists"
266        );
267    }
268}