Skip to main content

allsource_core/
error.rs

1use axum::http::StatusCode;
2use axum::response::{IntoResponse, Response};
3
4/// AllSource error types
5#[derive(Debug, thiserror::Error)]
6pub enum AllSourceError {
7    #[error("Event not found: {0}")]
8    EventNotFound(String),
9
10    #[error("Entity not found: {0}")]
11    EntityNotFound(String),
12
13    #[error("Tenant already exists: {0}")]
14    TenantAlreadyExists(String),
15
16    #[error("Tenant not found: {0}")]
17    TenantNotFound(String),
18
19    #[error("Invalid event: {0}")]
20    InvalidEvent(String),
21
22    #[error("Invalid query: {0}")]
23    InvalidQuery(String),
24
25    #[error("Invalid input: {0}")]
26    InvalidInput(String),
27
28    #[error("Storage error: {0}")]
29    StorageError(String),
30
31    #[error("Serialization error: {0}")]
32    SerializationError(#[from] serde_json::Error),
33
34    #[error("Arrow error: {0}")]
35    ArrowError(String),
36
37    #[error("Index error: {0}")]
38    IndexError(String),
39
40    #[error("Validation error: {0}")]
41    ValidationError(String),
42
43    #[error("Concurrency error: {0}")]
44    ConcurrencyError(String),
45
46    #[error("Queue full: {0}")]
47    QueueFull(String),
48
49    #[error("Internal error: {0}")]
50    InternalError(String),
51}
52
53// Alias for domain layer convenience
54pub use AllSourceError as Error;
55
56impl From<arrow::error::ArrowError> for AllSourceError {
57    fn from(err: arrow::error::ArrowError) -> Self {
58        AllSourceError::ArrowError(err.to_string())
59    }
60}
61
62impl From<parquet::errors::ParquetError> for AllSourceError {
63    fn from(err: parquet::errors::ParquetError) -> Self {
64        AllSourceError::StorageError(err.to_string())
65    }
66}
67
68impl From<crate::infrastructure::persistence::SimdJsonError> for AllSourceError {
69    fn from(err: crate::infrastructure::persistence::SimdJsonError) -> Self {
70        AllSourceError::SerializationError(serde_json::Error::io(std::io::Error::new(
71            std::io::ErrorKind::InvalidData,
72            err.to_string(),
73        )))
74    }
75}
76
77#[cfg(feature = "postgres")]
78impl From<sqlx::Error> for AllSourceError {
79    fn from(err: sqlx::Error) -> Self {
80        AllSourceError::StorageError(format!("Database error: {err}"))
81    }
82}
83
84/// Custom Result type for AllSource operations
85pub type Result<T> = std::result::Result<T, AllSourceError>;
86
87/// Implement IntoResponse for axum error handling
88impl IntoResponse for AllSourceError {
89    fn into_response(self) -> Response {
90        let (status, error_message) = match self {
91            AllSourceError::EventNotFound(_)
92            | AllSourceError::EntityNotFound(_)
93            | AllSourceError::TenantNotFound(_) => (StatusCode::NOT_FOUND, self.to_string()),
94            AllSourceError::InvalidEvent(_)
95            | AllSourceError::InvalidQuery(_)
96            | AllSourceError::InvalidInput(_)
97            | AllSourceError::ValidationError(_) => (StatusCode::BAD_REQUEST, self.to_string()),
98            AllSourceError::TenantAlreadyExists(_) | AllSourceError::ConcurrencyError(_) => {
99                (StatusCode::CONFLICT, self.to_string())
100            }
101            AllSourceError::QueueFull(_) => (StatusCode::SERVICE_UNAVAILABLE, self.to_string()),
102            AllSourceError::StorageError(_)
103            | AllSourceError::ArrowError(_)
104            | AllSourceError::IndexError(_)
105            | AllSourceError::InternalError(_) => {
106                (StatusCode::INTERNAL_SERVER_ERROR, self.to_string())
107            }
108            AllSourceError::SerializationError(_) => {
109                (StatusCode::UNPROCESSABLE_ENTITY, self.to_string())
110            }
111        };
112
113        let body = serde_json::json!({
114            "error": error_message,
115        });
116
117        (status, axum::Json(body)).into_response()
118    }
119}
120
121#[cfg(test)]
122mod tests {
123    use super::*;
124
125    #[test]
126    fn test_error_display() {
127        let err = AllSourceError::EventNotFound("event-123".to_string());
128        assert_eq!(err.to_string(), "Event not found: event-123");
129
130        let err = AllSourceError::EntityNotFound("entity-456".to_string());
131        assert_eq!(err.to_string(), "Entity not found: entity-456");
132
133        let err = AllSourceError::TenantAlreadyExists("tenant-1".to_string());
134        assert_eq!(err.to_string(), "Tenant already exists: tenant-1");
135
136        let err = AllSourceError::TenantNotFound("tenant-2".to_string());
137        assert_eq!(err.to_string(), "Tenant not found: tenant-2");
138    }
139
140    #[test]
141    fn test_error_variants() {
142        let errors: Vec<AllSourceError> = vec![
143            AllSourceError::InvalidEvent("bad event".to_string()),
144            AllSourceError::InvalidQuery("bad query".to_string()),
145            AllSourceError::InvalidInput("bad input".to_string()),
146            AllSourceError::StorageError("storage failed".to_string()),
147            AllSourceError::ArrowError("arrow failed".to_string()),
148            AllSourceError::IndexError("index failed".to_string()),
149            AllSourceError::ValidationError("validation failed".to_string()),
150            AllSourceError::ConcurrencyError("conflict".to_string()),
151            AllSourceError::QueueFull("queue full".to_string()),
152            AllSourceError::InternalError("internal error".to_string()),
153        ];
154
155        for err in errors {
156            let msg = err.to_string();
157            assert!(!msg.is_empty());
158        }
159    }
160
161    #[test]
162    fn test_into_response_not_found() {
163        let err = AllSourceError::EventNotFound("event-123".to_string());
164        let response = err.into_response();
165        assert_eq!(response.status(), StatusCode::NOT_FOUND);
166
167        let err = AllSourceError::EntityNotFound("entity-456".to_string());
168        let response = err.into_response();
169        assert_eq!(response.status(), StatusCode::NOT_FOUND);
170
171        let err = AllSourceError::TenantNotFound("tenant-1".to_string());
172        let response = err.into_response();
173        assert_eq!(response.status(), StatusCode::NOT_FOUND);
174    }
175
176    #[test]
177    fn test_into_response_bad_request() {
178        let err = AllSourceError::InvalidEvent("bad event".to_string());
179        let response = err.into_response();
180        assert_eq!(response.status(), StatusCode::BAD_REQUEST);
181
182        let err = AllSourceError::InvalidQuery("bad query".to_string());
183        let response = err.into_response();
184        assert_eq!(response.status(), StatusCode::BAD_REQUEST);
185
186        let err = AllSourceError::InvalidInput("bad input".to_string());
187        let response = err.into_response();
188        assert_eq!(response.status(), StatusCode::BAD_REQUEST);
189
190        let err = AllSourceError::ValidationError("validation failed".to_string());
191        let response = err.into_response();
192        assert_eq!(response.status(), StatusCode::BAD_REQUEST);
193    }
194
195    #[test]
196    fn test_into_response_conflict() {
197        let err = AllSourceError::TenantAlreadyExists("tenant-1".to_string());
198        let response = err.into_response();
199        assert_eq!(response.status(), StatusCode::CONFLICT);
200
201        let err = AllSourceError::ConcurrencyError("conflict".to_string());
202        let response = err.into_response();
203        assert_eq!(response.status(), StatusCode::CONFLICT);
204    }
205
206    #[test]
207    fn test_into_response_service_unavailable() {
208        let err = AllSourceError::QueueFull("queue is full".to_string());
209        let response = err.into_response();
210        assert_eq!(response.status(), StatusCode::SERVICE_UNAVAILABLE);
211    }
212
213    #[test]
214    fn test_into_response_internal_error() {
215        let err = AllSourceError::StorageError("storage error".to_string());
216        let response = err.into_response();
217        assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
218
219        let err = AllSourceError::ArrowError("arrow error".to_string());
220        let response = err.into_response();
221        assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
222
223        let err = AllSourceError::IndexError("index error".to_string());
224        let response = err.into_response();
225        assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
226
227        let err = AllSourceError::InternalError("internal error".to_string());
228        let response = err.into_response();
229        assert_eq!(response.status(), StatusCode::INTERNAL_SERVER_ERROR);
230    }
231
232    #[test]
233    fn test_from_arrow_error() {
234        let arrow_err = arrow::error::ArrowError::InvalidArgumentError("test".to_string());
235        let err: AllSourceError = arrow_err.into();
236        assert!(matches!(err, AllSourceError::ArrowError(_)));
237    }
238
239    #[test]
240    fn test_from_parquet_error() {
241        let parquet_err = parquet::errors::ParquetError::General("test".to_string());
242        let err: AllSourceError = parquet_err.into();
243        assert!(matches!(err, AllSourceError::StorageError(_)));
244    }
245
246    #[test]
247    fn test_error_debug() {
248        let err = AllSourceError::EventNotFound("test".to_string());
249        let debug_str = format!("{:?}", err);
250        assert!(debug_str.contains("EventNotFound"));
251    }
252}