1use axum::http::StatusCode;
2use axum::response::{IntoResponse, Response};
3
4#[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
53pub 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
84pub type Result<T> = std::result::Result<T, AllSourceError>;
86
87impl 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}