1pub type Result<T> = std::result::Result<T, Error>;
5
6#[derive(Debug, thiserror::Error)]
8pub enum Error {
9 #[error("Validation error: {message}")]
11 Validation {
12 message: String,
14 },
15
16 #[error("Routing error: {message}")]
18 Routing {
19 message: String,
21 },
22
23 #[error("Proxy error: {message}")]
25 Proxy {
26 message: String,
28 },
29
30 #[error("Latency simulation error: {message}")]
32 Latency {
33 message: String,
35 },
36
37 #[error("Configuration error: {message}")]
39 Config {
40 message: String,
42 },
43
44 #[error("Protocol not found: {message}")]
46 ProtocolNotFound {
47 message: String,
49 },
50
51 #[error("Protocol disabled: {message}")]
53 ProtocolDisabled {
54 message: String,
56 },
57
58 #[error("Protocol handler in use: {message}")]
60 ProtocolHandlerInUse {
61 message: String,
63 },
64
65 #[error("Protocol validation error: {message}")]
67 ProtocolValidationError {
68 protocol: String,
70 message: String,
72 },
73
74 #[error("IO error: {0}")]
76 Io(#[from] std::io::Error),
77
78 #[error("JSON error: {0}")]
80 Json(#[from] serde_json::Error),
81
82 #[error("YAML error: {0}")]
84 Yaml(#[from] serde_yaml::Error),
85
86 #[error("HTTP error: {0}")]
88 Http(#[from] reqwest::Error),
89
90 #[error("URL parse error: {0}")]
92 UrlParse(#[from] url::ParseError),
93
94 #[error("Regex error: {0}")]
96 Regex(#[from] regex::Error),
97
98 #[error("Route not found: {method} {path}")]
100 RouteNotFound {
101 method: String,
103 path: String,
105 },
106
107 #[error("Schema validation failed at '{path}': expected {expected}, got {actual}")]
109 SchemaValidationFailed {
110 path: String,
112 expected: String,
114 actual: String,
116 },
117
118 #[error("Configuration error: {message}")]
120 ConfigWithSource {
121 message: String,
123 #[source]
125 source: Box<dyn std::error::Error + Send + Sync>,
126 },
127
128 #[error("{entity} not found: {id}")]
130 NotFound {
131 entity: String,
133 id: String,
135 },
136
137 #[error("Feature disabled: {feature}")]
139 FeatureDisabled {
140 feature: String,
142 },
143
144 #[error("Already initialized: {component}")]
146 AlreadyInitialized {
147 component: String,
149 },
150
151 #[error("Invalid state: {message}")]
153 InvalidState {
154 message: String,
156 },
157
158 #[error("SIEM transport error: {message}")]
160 SiemTransport {
161 message: String,
163 },
164
165 #[error("I/O error ({context}): {message}")]
167 IoWithContext {
168 context: String,
170 message: String,
172 },
173
174 #[error("Internal error: {message}")]
176 Internal {
177 message: String,
179 },
180
181 #[error("Generic error: {0}")]
183 Generic(String),
184
185 #[error("Encryption error: {0}")]
187 Encryption(#[from] crate::encryption::EncryptionError),
188
189 #[cfg(feature = "scripting")]
191 #[error("JavaScript error: {0}")]
192 JavaScript(#[from] rquickjs::Error),
193}
194
195impl From<String> for Error {
196 fn from(message: String) -> Self {
197 Self::Generic(message)
198 }
199}
200
201impl Error {
202 pub fn validation<S: Into<String>>(message: S) -> Self {
204 Self::Validation {
205 message: message.into(),
206 }
207 }
208
209 pub fn routing<S: Into<String>>(message: S) -> Self {
211 Self::Routing {
212 message: message.into(),
213 }
214 }
215
216 pub fn proxy<S: Into<String>>(message: S) -> Self {
218 Self::Proxy {
219 message: message.into(),
220 }
221 }
222
223 pub fn latency<S: Into<String>>(message: S) -> Self {
225 Self::Latency {
226 message: message.into(),
227 }
228 }
229
230 pub fn config<S: Into<String>>(message: S) -> Self {
232 Self::Config {
233 message: message.into(),
234 }
235 }
236
237 pub fn protocol_not_found<S: Into<String>>(message: S) -> Self {
239 Self::ProtocolNotFound {
240 message: message.into(),
241 }
242 }
243
244 pub fn protocol_disabled<S: Into<String>>(message: S) -> Self {
246 Self::ProtocolDisabled {
247 message: message.into(),
248 }
249 }
250
251 pub fn protocol_handler_in_use<S: Into<String>>(message: S) -> Self {
253 Self::ProtocolHandlerInUse {
254 message: message.into(),
255 }
256 }
257
258 pub fn protocol_validation_error<S: Into<String>>(protocol: S, message: S) -> Self {
260 Self::ProtocolValidationError {
261 protocol: protocol.into(),
262 message: message.into(),
263 }
264 }
265
266 pub fn route_not_found<S: Into<String>>(method: S, path: S) -> Self {
268 Self::RouteNotFound {
269 method: method.into(),
270 path: path.into(),
271 }
272 }
273
274 pub fn schema_validation_failed<S: Into<String>>(path: S, expected: S, actual: S) -> Self {
276 Self::SchemaValidationFailed {
277 path: path.into(),
278 expected: expected.into(),
279 actual: actual.into(),
280 }
281 }
282
283 pub fn config_with_source<S: Into<String>>(
285 message: S,
286 source: impl std::error::Error + Send + Sync + 'static,
287 ) -> Self {
288 Self::ConfigWithSource {
289 message: message.into(),
290 source: Box::new(source),
291 }
292 }
293
294 pub fn not_found<S1: Into<String>, S2: Into<String>>(entity: S1, id: S2) -> Self {
296 Self::NotFound {
297 entity: entity.into(),
298 id: id.into(),
299 }
300 }
301
302 pub fn feature_disabled<S: Into<String>>(feature: S) -> Self {
304 Self::FeatureDisabled {
305 feature: feature.into(),
306 }
307 }
308
309 pub fn already_initialized<S: Into<String>>(component: S) -> Self {
311 Self::AlreadyInitialized {
312 component: component.into(),
313 }
314 }
315
316 pub fn invalid_state<S: Into<String>>(message: S) -> Self {
318 Self::InvalidState {
319 message: message.into(),
320 }
321 }
322
323 pub fn siem_transport<S: Into<String>>(message: S) -> Self {
325 Self::SiemTransport {
326 message: message.into(),
327 }
328 }
329
330 pub fn io_with_context<S1: Into<String>, S2: Into<String>>(context: S1, message: S2) -> Self {
332 Self::IoWithContext {
333 context: context.into(),
334 message: message.into(),
335 }
336 }
337
338 pub fn internal<S: Into<String>>(message: S) -> Self {
340 Self::Internal {
341 message: message.into(),
342 }
343 }
344
345 #[deprecated(note = "Use a specific error variant instead of Generic")]
347 pub fn generic<S: Into<String>>(message: S) -> Self {
348 Self::Generic(message.into())
349 }
350}
351
352#[cfg(test)]
353mod tests {
354 use super::*;
355
356 #[test]
357 fn test_validation_error() {
358 let err = Error::validation("test validation");
359 assert!(err.to_string().contains("Validation error"));
360 assert!(err.to_string().contains("test validation"));
361 }
362
363 #[test]
364 fn test_routing_error() {
365 let err = Error::routing("test routing");
366 assert!(err.to_string().contains("Routing error"));
367 assert!(err.to_string().contains("test routing"));
368 }
369
370 #[test]
371 fn test_proxy_error() {
372 let err = Error::proxy("test proxy");
373 assert!(err.to_string().contains("Proxy error"));
374 assert!(err.to_string().contains("test proxy"));
375 }
376
377 #[test]
378 fn test_latency_error() {
379 let err = Error::latency("test latency");
380 assert!(err.to_string().contains("Latency simulation error"));
381 assert!(err.to_string().contains("test latency"));
382 }
383
384 #[test]
385 fn test_config_error() {
386 let err = Error::config("test config");
387 assert!(err.to_string().contains("Configuration error"));
388 assert!(err.to_string().contains("test config"));
389 }
390
391 #[test]
392 fn test_internal_error() {
393 let err = Error::internal("test internal");
394 assert!(err.to_string().contains("Internal error"));
395 assert!(err.to_string().contains("test internal"));
396 }
397
398 #[test]
399 #[allow(deprecated)]
400 fn test_generic_error() {
401 let err = Error::generic("test generic");
402 assert!(err.to_string().contains("Generic error"));
403 assert!(err.to_string().contains("test generic"));
404 }
405
406 #[test]
407 fn test_not_found_error() {
408 let err = Error::not_found("User", "user-123");
409 assert_eq!(err.to_string(), "User not found: user-123");
410 }
411
412 #[test]
413 fn test_feature_disabled_error() {
414 let err = Error::feature_disabled("access review");
415 assert_eq!(err.to_string(), "Feature disabled: access review");
416 }
417
418 #[test]
419 fn test_already_initialized_error() {
420 let err = Error::already_initialized("SIEM emitter");
421 assert_eq!(err.to_string(), "Already initialized: SIEM emitter");
422 }
423
424 #[test]
425 fn test_invalid_state_error() {
426 let err = Error::invalid_state("Request is not pending approval");
427 assert_eq!(err.to_string(), "Invalid state: Request is not pending approval");
428 }
429
430 #[test]
431 fn test_siem_transport_error() {
432 let err = Error::siem_transport("Connection refused");
433 assert_eq!(err.to_string(), "SIEM transport error: Connection refused");
434 }
435
436 #[test]
437 fn test_io_with_context_error() {
438 let err = Error::io_with_context("reading risk register", "file not found");
439 assert_eq!(err.to_string(), "I/O error (reading risk register): file not found");
440 }
441
442 #[test]
443 fn test_from_string() {
444 let err: Error = "test message".to_string().into();
445 assert!(matches!(err, Error::Generic(_)));
446 assert!(err.to_string().contains("test message"));
447 }
448
449 #[test]
450 fn test_json_error_conversion() {
451 let json_err = serde_json::from_str::<serde_json::Value>("invalid json");
452 assert!(json_err.is_err());
453 let err: Error = json_err.unwrap_err().into();
454 assert!(matches!(err, Error::Json(_)));
455 }
456
457 #[test]
458 fn test_url_parse_error_conversion() {
459 let url_err = url::Url::parse("not a url");
460 assert!(url_err.is_err());
461 let err: Error = url_err.unwrap_err().into();
462 assert!(matches!(err, Error::UrlParse(_)));
463 }
464
465 #[test]
466 #[allow(clippy::invalid_regex)]
467 fn test_regex_error_conversion() {
468 let regex_err = regex::Regex::new("[invalid(");
469 assert!(regex_err.is_err());
470 let err: Error = regex_err.unwrap_err().into();
471 assert!(matches!(err, Error::Regex(_)));
472 }
473
474 #[test]
475 #[allow(deprecated)]
476 fn test_error_display() {
477 let errors = vec![
478 (Error::validation("msg"), "Validation error: msg"),
479 (Error::routing("msg"), "Routing error: msg"),
480 (Error::proxy("msg"), "Proxy error: msg"),
481 (Error::latency("msg"), "Latency simulation error: msg"),
482 (Error::config("msg"), "Configuration error: msg"),
483 (Error::internal("msg"), "Internal error: msg"),
484 ];
485
486 for (err, expected) in errors {
487 assert_eq!(err.to_string(), expected);
488 }
489 }
490}