1use thiserror::Error;
4
5use crate::core::license::LicenseError;
6
7pub type Result<T> = std::result::Result<T, PhyTraceError>;
9
10pub type PhyTraceResult<T> = Result<T>;
12
13#[derive(Debug, Error)]
15pub enum PhyTraceError {
16 #[error("Builder error: {0}")]
18 Builder(#[from] BuilderError),
19
20 #[error("Validation error: {0}")]
22 Validation(String),
23
24 #[error("Transport error: {0}")]
26 Transport(String),
27
28 #[error("Buffer error: {0}")]
30 Buffer(#[from] BufferError),
31
32 #[error("Configuration error: {0}")]
34 Config(#[from] ConfigError),
35
36 #[error("License error: {0}")]
38 License(#[from] LicenseError),
39
40 #[error("Serialization error: {0}")]
42 Serialization(String),
43
44 #[error("YAML error: {0}")]
46 Yaml(#[from] serde_yaml::Error),
47
48 #[error("IO error: {0}")]
50 Io(#[from] std::io::Error),
51
52 #[error("Provenance error: {0}")]
54 Provenance(String),
55
56 #[error("Agent error: {0}")]
58 Agent(String),
59
60 #[error("Timeout: {0}")]
62 Timeout(String),
63
64 #[error("Crypto error: {0}")]
66 Crypto(String),
67}
68
69#[derive(Debug, Error)]
71pub enum BuilderError {
72 #[error("Missing required field: {0}")]
74 MissingField(String),
75
76 #[error("Invalid value for field '{field}': {message}")]
78 InvalidValue {
79 field: String,
81 message: String,
83 },
84
85 #[error("Validation failed: {0}")]
87 ValidationFailed(String),
88}
89
90#[derive(Debug, Error)]
92pub enum BufferError {
93 #[error("Buffer full: {0}")]
95 Full(String),
96
97 #[error("IO error: {0}")]
99 Io(String),
100
101 #[error("Serialization error: {0}")]
103 Serialization(String),
104
105 #[error("Buffer corrupted: {0}")]
107 Corrupted(String),
108}
109
110#[derive(Debug, Error)]
112pub enum ConfigError {
113 #[error("Failed to read config file: {0}")]
115 FileRead(String),
116
117 #[error("Failed to parse config: {0}")]
119 Parse(String),
120
121 #[error("Config validation error: {0}")]
123 Validation(String),
124
125 #[error("Missing required config: {0}")]
127 MissingRequired(String),
128}
129
130#[derive(Debug, Clone, Copy, PartialEq, Eq)]
132pub enum ErrorCode {
133 MissingRequiredField,
136 InvalidFieldType,
138 ValueOutOfRange,
140 InvalidEnumValue,
142
143 NetworkUnreachable,
146 ConnectionTimeout,
148 TlsHandshakeFailed,
150 RateLimited,
152
153 InvalidApiKey,
156 TokenExpired,
158 InsufficientPermissions,
160
161 BufferFull,
164 DiskWriteFailed,
166 EncryptionError,
168
169 PlatformConnectionFailed,
172 TopicNotFound,
174 MessageParseError,
176}
177
178impl ErrorCode {
179 pub fn code(&self) -> &'static str {
181 match self {
182 Self::MissingRequiredField => "E001",
183 Self::InvalidFieldType => "E002",
184 Self::ValueOutOfRange => "E003",
185 Self::InvalidEnumValue => "E004",
186 Self::NetworkUnreachable => "E010",
187 Self::ConnectionTimeout => "E011",
188 Self::TlsHandshakeFailed => "E012",
189 Self::RateLimited => "E013",
190 Self::InvalidApiKey => "E020",
191 Self::TokenExpired => "E021",
192 Self::InsufficientPermissions => "E022",
193 Self::BufferFull => "E030",
194 Self::DiskWriteFailed => "E031",
195 Self::EncryptionError => "E032",
196 Self::PlatformConnectionFailed => "E040",
197 Self::TopicNotFound => "E041",
198 Self::MessageParseError => "E042",
199 }
200 }
201
202 pub fn description(&self) -> &'static str {
204 match self {
205 Self::MissingRequiredField => "Missing required field",
206 Self::InvalidFieldType => "Invalid field type",
207 Self::ValueOutOfRange => "Value out of range",
208 Self::InvalidEnumValue => "Invalid enum value",
209 Self::NetworkUnreachable => "Network unreachable",
210 Self::ConnectionTimeout => "Connection timeout",
211 Self::TlsHandshakeFailed => "TLS handshake failed",
212 Self::RateLimited => "Rate limited",
213 Self::InvalidApiKey => "Invalid API key",
214 Self::TokenExpired => "Token expired",
215 Self::InsufficientPermissions => "Insufficient permissions",
216 Self::BufferFull => "Buffer full",
217 Self::DiskWriteFailed => "Disk write failed",
218 Self::EncryptionError => "Encryption error",
219 Self::PlatformConnectionFailed => "Platform connection failed",
220 Self::TopicNotFound => "Topic not found",
221 Self::MessageParseError => "Message parse error",
222 }
223 }
224}
225
226impl std::fmt::Display for ErrorCode {
227 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
228 write!(f, "{}: {}", self.code(), self.description())
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235
236 #[test]
241 fn test_error_code_codes() {
242 assert_eq!(ErrorCode::MissingRequiredField.code(), "E001");
244 assert_eq!(ErrorCode::InvalidFieldType.code(), "E002");
245 assert_eq!(ErrorCode::ValueOutOfRange.code(), "E003");
246 assert_eq!(ErrorCode::InvalidEnumValue.code(), "E004");
247
248 assert_eq!(ErrorCode::NetworkUnreachable.code(), "E010");
250 assert_eq!(ErrorCode::ConnectionTimeout.code(), "E011");
251 assert_eq!(ErrorCode::TlsHandshakeFailed.code(), "E012");
252 assert_eq!(ErrorCode::RateLimited.code(), "E013");
253
254 assert_eq!(ErrorCode::InvalidApiKey.code(), "E020");
256 assert_eq!(ErrorCode::TokenExpired.code(), "E021");
257 assert_eq!(ErrorCode::InsufficientPermissions.code(), "E022");
258
259 assert_eq!(ErrorCode::BufferFull.code(), "E030");
261 assert_eq!(ErrorCode::DiskWriteFailed.code(), "E031");
262 assert_eq!(ErrorCode::EncryptionError.code(), "E032");
263
264 assert_eq!(ErrorCode::PlatformConnectionFailed.code(), "E040");
266 assert_eq!(ErrorCode::TopicNotFound.code(), "E041");
267 assert_eq!(ErrorCode::MessageParseError.code(), "E042");
268 }
269
270 #[test]
271 fn test_error_code_descriptions() {
272 assert_eq!(
274 ErrorCode::MissingRequiredField.description(),
275 "Missing required field"
276 );
277 assert_eq!(
278 ErrorCode::InvalidFieldType.description(),
279 "Invalid field type"
280 );
281 assert_eq!(
282 ErrorCode::ValueOutOfRange.description(),
283 "Value out of range"
284 );
285 assert_eq!(
286 ErrorCode::InvalidEnumValue.description(),
287 "Invalid enum value"
288 );
289
290 assert_eq!(
292 ErrorCode::NetworkUnreachable.description(),
293 "Network unreachable"
294 );
295 assert_eq!(
296 ErrorCode::ConnectionTimeout.description(),
297 "Connection timeout"
298 );
299 assert_eq!(
300 ErrorCode::TlsHandshakeFailed.description(),
301 "TLS handshake failed"
302 );
303 assert_eq!(ErrorCode::RateLimited.description(), "Rate limited");
304
305 assert_eq!(ErrorCode::InvalidApiKey.description(), "Invalid API key");
307 assert_eq!(ErrorCode::TokenExpired.description(), "Token expired");
308 assert_eq!(
309 ErrorCode::InsufficientPermissions.description(),
310 "Insufficient permissions"
311 );
312
313 assert_eq!(ErrorCode::BufferFull.description(), "Buffer full");
315 assert_eq!(
316 ErrorCode::DiskWriteFailed.description(),
317 "Disk write failed"
318 );
319 assert_eq!(ErrorCode::EncryptionError.description(), "Encryption error");
320
321 assert_eq!(
323 ErrorCode::PlatformConnectionFailed.description(),
324 "Platform connection failed"
325 );
326 assert_eq!(ErrorCode::TopicNotFound.description(), "Topic not found");
327 assert_eq!(
328 ErrorCode::MessageParseError.description(),
329 "Message parse error"
330 );
331 }
332
333 #[test]
334 fn test_error_code_display() {
335 assert_eq!(
336 format!("{}", ErrorCode::MissingRequiredField),
337 "E001: Missing required field"
338 );
339 assert_eq!(
340 format!("{}", ErrorCode::NetworkUnreachable),
341 "E010: Network unreachable"
342 );
343 assert_eq!(
344 format!("{}", ErrorCode::InvalidApiKey),
345 "E020: Invalid API key"
346 );
347 assert_eq!(format!("{}", ErrorCode::BufferFull), "E030: Buffer full");
348 assert_eq!(
349 format!("{}", ErrorCode::PlatformConnectionFailed),
350 "E040: Platform connection failed"
351 );
352 }
353
354 #[test]
355 fn test_error_code_equality() {
356 assert_eq!(
357 ErrorCode::MissingRequiredField,
358 ErrorCode::MissingRequiredField
359 );
360 assert_ne!(ErrorCode::MissingRequiredField, ErrorCode::InvalidFieldType);
361 }
362
363 #[test]
364 fn test_error_code_clone() {
365 let code = ErrorCode::NetworkUnreachable;
366 let cloned = code;
367 assert_eq!(code, cloned);
368 }
369
370 #[test]
371 fn test_error_code_debug() {
372 let debug_str = format!("{:?}", ErrorCode::ConnectionTimeout);
373 assert!(debug_str.contains("ConnectionTimeout"));
374 }
375
376 #[test]
381 fn test_builder_error_missing_field() {
382 let err = BuilderError::MissingField("source_id".to_string());
383 assert!(format!("{}", err).contains("source_id"));
384 assert!(format!("{}", err).contains("Missing required field"));
385 }
386
387 #[test]
388 fn test_builder_error_invalid_value() {
389 let err = BuilderError::InvalidValue {
390 field: "latitude".to_string(),
391 message: "must be between -90 and 90".to_string(),
392 };
393 let display = format!("{}", err);
394 assert!(display.contains("latitude"));
395 assert!(display.contains("must be between -90 and 90"));
396 }
397
398 #[test]
399 fn test_builder_error_validation_failed() {
400 let err = BuilderError::ValidationFailed("Event has no domain data".to_string());
401 assert!(format!("{}", err).contains("Validation failed"));
402 assert!(format!("{}", err).contains("Event has no domain data"));
403 }
404
405 #[test]
406 fn test_builder_error_debug() {
407 let err = BuilderError::MissingField("test".to_string());
408 let debug_str = format!("{:?}", err);
409 assert!(debug_str.contains("MissingField"));
410 }
411
412 #[test]
417 fn test_buffer_error_full() {
418 let err = BufferError::Full("Buffer capacity exceeded".to_string());
419 assert!(format!("{}", err).contains("Buffer full"));
420 assert!(format!("{}", err).contains("Buffer capacity exceeded"));
421 }
422
423 #[test]
424 fn test_buffer_error_io() {
425 let err = BufferError::Io("Failed to write to disk".to_string());
426 assert!(format!("{}", err).contains("IO error"));
427 assert!(format!("{}", err).contains("Failed to write to disk"));
428 }
429
430 #[test]
431 fn test_buffer_error_serialization() {
432 let err = BufferError::Serialization("Invalid JSON".to_string());
433 assert!(format!("{}", err).contains("Serialization error"));
434 assert!(format!("{}", err).contains("Invalid JSON"));
435 }
436
437 #[test]
438 fn test_buffer_error_corrupted() {
439 let err = BufferError::Corrupted("Checksum mismatch".to_string());
440 assert!(format!("{}", err).contains("Buffer corrupted"));
441 assert!(format!("{}", err).contains("Checksum mismatch"));
442 }
443
444 #[test]
449 fn test_config_error_file_read() {
450 let err = ConfigError::FileRead("/path/to/config.yaml".to_string());
451 assert!(format!("{}", err).contains("Failed to read config file"));
452 }
453
454 #[test]
455 fn test_config_error_parse() {
456 let err = ConfigError::Parse("Invalid YAML syntax".to_string());
457 assert!(format!("{}", err).contains("Failed to parse config"));
458 }
459
460 #[test]
461 fn test_config_error_validation() {
462 let err = ConfigError::Validation("endpoint is required".to_string());
463 assert!(format!("{}", err).contains("Config validation error"));
464 }
465
466 #[test]
467 fn test_config_error_missing_required() {
468 let err = ConfigError::MissingRequired("api_key".to_string());
469 assert!(format!("{}", err).contains("Missing required config"));
470 }
471
472 #[test]
477 fn test_phytrace_error_builder() {
478 let builder_err = BuilderError::MissingField("test".to_string());
479 let err: PhyTraceError = builder_err.into();
480 assert!(format!("{}", err).contains("Builder error"));
481 }
482
483 #[test]
484 fn test_phytrace_error_validation() {
485 let err = PhyTraceError::Validation("Invalid latitude".to_string());
486 assert!(format!("{}", err).contains("Validation error"));
487 assert!(format!("{}", err).contains("Invalid latitude"));
488 }
489
490 #[test]
491 fn test_phytrace_error_transport() {
492 let err = PhyTraceError::Transport("Connection refused".to_string());
493 assert!(format!("{}", err).contains("Transport error"));
494 assert!(format!("{}", err).contains("Connection refused"));
495 }
496
497 #[test]
498 fn test_phytrace_error_buffer() {
499 let buffer_err = BufferError::Full("capacity".to_string());
500 let err: PhyTraceError = buffer_err.into();
501 assert!(format!("{}", err).contains("Buffer error"));
502 }
503
504 #[test]
505 fn test_phytrace_error_config() {
506 let config_err = ConfigError::Validation("invalid".to_string());
507 let err: PhyTraceError = config_err.into();
508 assert!(format!("{}", err).contains("Configuration error"));
509 }
510
511 #[test]
512 fn test_phytrace_error_serialization() {
513 let err = PhyTraceError::Serialization("JSON error".to_string());
514 assert!(format!("{}", err).contains("Serialization error"));
515 }
516
517 #[test]
518 fn test_phytrace_error_io() {
519 let io_err = std::io::Error::new(std::io::ErrorKind::NotFound, "file not found");
520 let err: PhyTraceError = io_err.into();
521 assert!(format!("{}", err).contains("IO error"));
522 }
523
524 #[test]
525 fn test_phytrace_error_provenance() {
526 let err = PhyTraceError::Provenance("Invalid signature".to_string());
527 assert!(format!("{}", err).contains("Provenance error"));
528 }
529
530 #[test]
531 fn test_phytrace_error_agent() {
532 let err = PhyTraceError::Agent("Agent not started".to_string());
533 assert!(format!("{}", err).contains("Agent error"));
534 }
535
536 #[test]
537 fn test_phytrace_error_timeout() {
538 let err = PhyTraceError::Timeout("Request timed out after 30s".to_string());
539 assert!(format!("{}", err).contains("Timeout"));
540 }
541
542 #[test]
543 fn test_phytrace_error_crypto() {
544 let err = PhyTraceError::Crypto("Invalid key length".to_string());
545 assert!(format!("{}", err).contains("Crypto error"));
546 }
547
548 #[test]
549 fn test_phytrace_error_debug() {
550 let err = PhyTraceError::Transport("test".to_string());
551 let debug_str = format!("{:?}", err);
552 assert!(debug_str.contains("Transport"));
553 }
554
555 #[test]
560 fn test_result_type_alias() {
561 fn returns_result() -> Result<i32> {
562 Ok(42)
563 }
564 assert_eq!(returns_result().unwrap(), 42);
565 }
566
567 #[test]
568 fn test_phytrace_result_type_alias() {
569 fn returns_phytrace_result() -> PhyTraceResult<String> {
570 Ok("success".to_string())
571 }
572 assert_eq!(returns_phytrace_result().unwrap(), "success");
573 }
574
575 #[test]
576 fn test_result_error() {
577 fn returns_error() -> Result<()> {
578 Err(PhyTraceError::Validation("test".to_string()))
579 }
580 assert!(returns_error().is_err());
581 }
582}