elif_http/errors/
http_error.rs1use thiserror::Error;
7
8pub type HttpResult<T> = Result<T, HttpError>;
10
11#[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 pub fn startup<T: Into<String>>(message: T) -> Self {
63 HttpError::StartupFailed {
64 message: message.into()
65 }
66 }
67
68 pub fn shutdown<T: Into<String>>(message: T) -> Self {
70 HttpError::ShutdownFailed {
71 message: message.into()
72 }
73 }
74
75 pub fn config<T: Into<String>>(message: T) -> Self {
77 HttpError::ConfigError {
78 message: message.into()
79 }
80 }
81
82 pub fn service_resolution<T: Into<String>>(service: T) -> Self {
84 HttpError::ServiceResolutionFailed {
85 service: service.into()
86 }
87 }
88
89 pub fn bad_request<T: Into<String>>(message: T) -> Self {
91 HttpError::BadRequest {
92 message: message.into()
93 }
94 }
95
96 pub fn internal<T: Into<String>>(message: T) -> Self {
98 HttpError::InternalError {
99 message: message.into()
100 }
101 }
102
103 pub fn health_check<T: Into<String>>(reason: T) -> Self {
105 HttpError::HealthCheckFailed {
106 reason: reason.into()
107 }
108 }
109
110 pub fn database_error<T: Into<String>>(message: T) -> Self {
112 HttpError::DatabaseError {
113 message: message.into()
114 }
115 }
116
117 pub fn validation_error<T: Into<String>>(message: T) -> Self {
119 HttpError::ValidationError {
120 message: message.into()
121 }
122 }
123
124 pub fn not_found<T: Into<String>>(resource: T) -> Self {
126 HttpError::NotFound {
127 resource: resource.into()
128 }
129 }
130
131 pub fn conflict<T: Into<String>>(message: T) -> Self {
133 HttpError::Conflict {
134 message: message.into()
135 }
136 }
137
138 pub fn unauthorized() -> Self {
140 HttpError::Unauthorized
141 }
142
143 pub fn forbidden<T: Into<String>>(message: T) -> Self {
145 HttpError::Forbidden {
146 message: message.into()
147 }
148 }
149
150 pub fn timeout() -> Self {
152 HttpError::RequestTimeout
153 }
154
155 pub fn payload_too_large(size: usize, limit: usize) -> Self {
157 HttpError::RequestTooLarge { size, limit }
158 }
159
160
161 pub fn with_detail<T: Into<String>>(self, _detail: T) -> Self {
163 self
164 }
165
166 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
188impl 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
197impl 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
206impl 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
215impl 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#[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#[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}