elif_http/errors/
http_error.rs

1//! HTTP server error types
2//! 
3//! Comprehensive error handling for HTTP operations, integrating with
4//! the elif framework error system.
5
6use thiserror::Error;
7
8/// Result type for HTTP operations
9pub type HttpResult<T> = Result<T, HttpError>;
10
11/// HTTP server errors
12#[derive(Error, Debug)]
13pub enum HttpError {
14    #[error("Server startup failed: {message}")]
15    StartupFailed { message: String },
16    
17    #[error("Server shutdown failed: {message}")]
18    ShutdownFailed { message: String },
19    
20    #[error("Configuration error: {message}")]
21    ConfigError { message: String },
22    
23    #[error("Service resolution failed: {service}")]
24    ServiceResolutionFailed { service: String },
25    
26    #[error("Request timeout")]
27    RequestTimeout,
28    
29    #[error("Request too large: {size} bytes exceeds limit of {limit} bytes")]
30    RequestTooLarge { size: usize, limit: usize },
31    
32    #[error("Invalid request: {message}")]
33    BadRequest { message: String },
34    
35    #[error("Internal server error: {message}")]
36    InternalError { message: String },
37    
38    #[error("Health check failed: {reason}")]
39    HealthCheckFailed { reason: String },
40    
41    #[error("Database error: {message}")]
42    DatabaseError { message: String },
43    
44    #[error("Validation error: {message}")]
45    ValidationError { message: String },
46    
47    #[error("Resource not found: {resource}")]
48    NotFound { resource: String },
49    
50    #[error("Resource already exists: {message}")]
51    Conflict { message: String },
52    
53    #[error("Unauthorized access")]
54    Unauthorized,
55    
56    #[error("Access forbidden: {message}")]
57    Forbidden { message: String },
58}
59
60impl HttpError {
61    /// Create a startup error
62    pub fn startup<T: Into<String>>(message: T) -> Self {
63        HttpError::StartupFailed { 
64            message: message.into() 
65        }
66    }
67    
68    /// Create a shutdown error
69    pub fn shutdown<T: Into<String>>(message: T) -> Self {
70        HttpError::ShutdownFailed { 
71            message: message.into() 
72        }
73    }
74    
75    /// Create a configuration error
76    pub fn config<T: Into<String>>(message: T) -> Self {
77        HttpError::ConfigError { 
78            message: message.into() 
79        }
80    }
81    
82    /// Create a service resolution error
83    pub fn service_resolution<T: Into<String>>(service: T) -> Self {
84        HttpError::ServiceResolutionFailed { 
85            service: service.into() 
86        }
87    }
88    
89    /// Create a bad request error
90    pub fn bad_request<T: Into<String>>(message: T) -> Self {
91        HttpError::BadRequest { 
92            message: message.into() 
93        }
94    }
95
96    /// Create an internal error
97    pub fn internal<T: Into<String>>(message: T) -> Self {
98        HttpError::InternalError { 
99            message: message.into() 
100        }
101    }
102    
103    /// Create a health check error
104    pub fn health_check<T: Into<String>>(reason: T) -> Self {
105        HttpError::HealthCheckFailed { 
106            reason: reason.into() 
107        }
108    }
109    
110    /// Create a database error
111    pub fn database_error<T: Into<String>>(message: T) -> Self {
112        HttpError::DatabaseError { 
113            message: message.into() 
114        }
115    }
116    
117    /// Create a validation error
118    pub fn validation_error<T: Into<String>>(message: T) -> Self {
119        HttpError::ValidationError { 
120            message: message.into() 
121        }
122    }
123    
124    /// Create a not found error
125    pub fn not_found<T: Into<String>>(resource: T) -> Self {
126        HttpError::NotFound { 
127            resource: resource.into() 
128        }
129    }
130    
131    /// Create a conflict error
132    pub fn conflict<T: Into<String>>(message: T) -> Self {
133        HttpError::Conflict { 
134            message: message.into() 
135        }
136    }
137    
138    /// Create an unauthorized error
139    pub fn unauthorized() -> Self {
140        HttpError::Unauthorized
141    }
142    
143    /// Create a forbidden error
144    pub fn forbidden<T: Into<String>>(message: T) -> Self {
145        HttpError::Forbidden { 
146            message: message.into() 
147        }
148    }
149
150    /// Create a timeout error
151    pub fn timeout() -> Self {
152        HttpError::RequestTimeout
153    }
154
155    /// Create a payload too large error with specific sizes
156    pub fn payload_too_large(size: usize, limit: usize) -> Self {
157        HttpError::RequestTooLarge { size, limit }
158    }
159
160
161    /// Add additional detail to error (for now, just returns self - future enhancement)
162    pub fn with_detail<T: Into<String>>(self, _detail: T) -> Self {
163        self
164    }
165
166    /// Get error code for consistent API responses
167    pub fn error_code(&self) -> &'static str {
168        match self {
169            HttpError::StartupFailed { .. } => "SERVER_STARTUP_FAILED",
170            HttpError::ShutdownFailed { .. } => "SERVER_SHUTDOWN_FAILED",
171            HttpError::ConfigError { .. } => "CONFIGURATION_ERROR",
172            HttpError::ServiceResolutionFailed { .. } => "SERVICE_RESOLUTION_FAILED",
173            HttpError::RequestTimeout => "REQUEST_TIMEOUT",
174            HttpError::RequestTooLarge { .. } => "REQUEST_TOO_LARGE",
175            HttpError::BadRequest { .. } => "BAD_REQUEST",
176            HttpError::InternalError { .. } => "INTERNAL_ERROR",
177            HttpError::HealthCheckFailed { .. } => "HEALTH_CHECK_FAILED",
178            HttpError::DatabaseError { .. } => "DATABASE_ERROR",
179            HttpError::ValidationError { .. } => "VALIDATION_ERROR",
180            HttpError::NotFound { .. } => "RESOURCE_NOT_FOUND",
181            HttpError::Conflict { .. } => "RESOURCE_CONFLICT",
182            HttpError::Unauthorized => "UNAUTHORIZED_ACCESS",
183            HttpError::Forbidden { .. } => "ACCESS_FORBIDDEN",
184        }
185    }
186}
187
188// Convert from elif-core ConfigError
189impl From<elif_core::ConfigError> for HttpError {
190    fn from(err: elif_core::ConfigError) -> Self {
191        HttpError::ConfigError { 
192            message: err.to_string() 
193        }
194    }
195}
196
197// Convert from std::io::Error
198impl From<std::io::Error> for HttpError {
199    fn from(err: std::io::Error) -> Self {
200        HttpError::InternalError { 
201            message: format!("IO error: {}", err) 
202        }
203    }
204}
205
206// Convert from hyper errors
207impl From<hyper::Error> for HttpError {
208    fn from(err: hyper::Error) -> Self {
209        HttpError::InternalError { 
210            message: format!("Hyper error: {}", err) 
211        }
212    }
213}
214
215// Convert from serde_json errors
216impl From<serde_json::Error> for HttpError {
217    fn from(err: serde_json::Error) -> Self {
218        HttpError::InternalError { 
219            message: format!("JSON serialization error: {}", err) 
220        }
221    }
222}
223
224// Convert from ORM ModelError to HttpError
225#[cfg(feature = "orm")]
226impl From<orm::ModelError> for HttpError {
227    fn from(err: orm::ModelError) -> Self {
228        match err {
229            orm::ModelError::NotFound(table) => HttpError::NotFound { 
230                resource: table 
231            },
232            orm::ModelError::Validation(msg) => HttpError::ValidationError { 
233                message: msg 
234            },
235            orm::ModelError::Database(msg) => HttpError::DatabaseError { 
236                message: msg 
237            },
238            orm::ModelError::Connection(msg) => HttpError::DatabaseError { 
239                message: format!("Connection error: {}", msg) 
240            },
241            orm::ModelError::Transaction(msg) => HttpError::DatabaseError { 
242                message: format!("Transaction error: {}", msg) 
243            },
244            orm::ModelError::Query(msg) => HttpError::BadRequest { 
245                message: format!("Query error: {}", msg) 
246            },
247            orm::ModelError::Schema(msg) => HttpError::InternalError { 
248                message: format!("Schema error: {}", msg) 
249            },
250            orm::ModelError::Migration(msg) => HttpError::InternalError { 
251                message: format!("Migration error: {}", msg) 
252            },
253            orm::ModelError::MissingPrimaryKey => HttpError::BadRequest { 
254                message: "Missing or invalid primary key".to_string() 
255            },
256            orm::ModelError::Relationship(msg) => HttpError::BadRequest { 
257                message: format!("Relationship error: {}", msg) 
258            },
259            orm::ModelError::Serialization(msg) => HttpError::InternalError { 
260                message: format!("Serialization error: {}", msg) 
261            },
262            orm::ModelError::Event(msg) => HttpError::InternalError { 
263                message: format!("Event error: {}", msg) 
264            },
265        }
266    }
267}
268
269// Convert from ORM QueryError to HttpError
270#[cfg(feature = "orm")]
271impl From<orm::QueryError> for HttpError {
272    fn from(err: orm::QueryError) -> Self {
273        match err {
274            orm::QueryError::InvalidSql(msg) => HttpError::BadRequest { 
275                message: format!("Invalid SQL query: {}", msg) 
276            },
277            orm::QueryError::MissingFields(msg) => HttpError::BadRequest { 
278                message: format!("Missing required fields: {}", msg) 
279            },
280            orm::QueryError::InvalidParameter(msg) => HttpError::BadRequest { 
281                message: format!("Invalid query parameter: {}", msg) 
282            },
283            orm::QueryError::UnsupportedOperation(msg) => HttpError::BadRequest { 
284                message: format!("Unsupported operation: {}", msg) 
285            },
286        }
287    }
288}
289
290#[cfg(test)]
291mod tests {
292    use super::*;
293
294    #[test]
295    fn test_error_creation() {
296        let error = HttpError::startup("Failed to bind to port");
297        assert!(matches!(error, HttpError::StartupFailed { .. }));
298        assert_eq!(error.error_code(), "SERVER_STARTUP_FAILED");
299    }
300
301    #[test]
302    fn test_error_codes() {
303        assert_eq!(HttpError::bad_request("test").error_code(), "BAD_REQUEST");
304        assert_eq!(HttpError::RequestTimeout.error_code(), "REQUEST_TIMEOUT");
305        assert_eq!(HttpError::internal("test").error_code(), "INTERNAL_ERROR");
306    }
307
308    #[test]
309    fn test_config_error_conversion() {
310        let config_error = elif_core::ConfigError::validation_failed("Test validation error");
311        let http_error = HttpError::from(config_error);
312        assert!(matches!(http_error, HttpError::ConfigError { .. }));
313    }
314
315    #[test]
316    fn test_io_error_conversion() {
317        let io_error = std::io::Error::new(std::io::ErrorKind::PermissionDenied, "Access denied");
318        let http_error = HttpError::from(io_error);
319        assert!(matches!(http_error, HttpError::InternalError { .. }));
320    }
321
322    #[test]
323    fn test_validation_error_creation() {
324        let validation_error = HttpError::validation_error("Field is required");
325        assert_eq!(validation_error.error_code(), "VALIDATION_ERROR");
326    }
327}