//! Error types for the data module use std::fmt; use thiserror::Error; /// Result type alias for data module operations pub type Result = std::result::Result; /// Data module error types #[derive(Error, Debug)] pub enum DataError { /// Network connectivity errors #[error("Network error: {message}")] Network { message: String }, /// FIX protocol errors #[error("FIX protocol error: {message}")] FixProtocol { message: String }, /// Authentication errors #[error("Authentication error: {message}")] Authentication { message: String }, /// Configuration errors #[error("Configuration error in field '{field}': {message}")] Configuration { field: String, message: String }, /// Message parsing errors #[error("Message parsing error: {message}")] MessageParsing { message: String }, /// Session management errors #[error("Session error: {message}")] Session { message: String }, /// Order management errors #[error("Order error: {message}")] Order { message: String }, /// Timeout errors #[error("Operation timed out: {message}")] Timeout { message: String }, /// Parse errors #[error("Parse error: {message}")] Parse { message: String }, /// Validation errors (consolidated) #[error("Validation error in field '{field}': {message}")] Validation { field: String, message: String }, /// Simple validation error #[error("Validation error: {0}")] ValidationSimple(String), /// Serialization errors (consolidated) #[error("Serialization error: {message}")] Serialization { message: String }, /// Compression errors #[error("Compression error: {0}")] Compression(String), /// Storage errors #[error("Storage error: {0}")] Storage(String), /// Dataset not found #[error("Not found: {0}")] NotFound(String), /// Broker-specific errors #[error("Broker error: {message}")] Broker { message: String }, /// Connection errors #[error("Connection error: {0}")] Connection(String), /// API errors #[error("API error: {message} (status: {status:?})")] Api { message: String, status: Option, }, /// Invalid parameter errors #[error("Invalid parameter '{field}': {message}")] InvalidParameter { field: String, message: String }, /// Unsupported operation errors #[error("Unsupported operation: {0}")] Unsupported(String), /// Not implemented errors #[error("Not implemented: {0}")] NotImplemented(String), /// Initialization errors #[error("Initialization error: {0}")] Initialization(String), /// Invalid format errors #[error("Invalid format: {0}")] InvalidFormat(String), /// Conversion errors #[error("Conversion error: {0}")] Conversion(String), /// Rate limiting errors #[error("Rate limit exceeded")] RateLimit, // External error types with automatic From trait generation /// I/O errors #[error(transparent)] Io(#[from] std::io::Error), /// JSON serialization/deserialization errors #[error(transparent)] Json(#[from] serde_json::Error), /// HTTP client errors #[error(transparent)] Http(#[from] reqwest::Error), /// WebSocket errors #[error(transparent)] WebSocket(#[from] tokio_tungstenite::tungstenite::Error), /// Time parsing errors #[error(transparent)] Time(#[from] chrono::ParseError), /// URL parsing errors #[error(transparent)] Url(#[from] url::ParseError), /// Bincode serialization errors #[error("Bincode error: {0}")] Bincode(#[from] bincode::Error), /// Parquet file errors #[error("Parquet error: {0}")] Parquet(#[from] parquet::errors::ParquetError), /// Arrow data errors #[error("Arrow error: {0}")] Arrow(#[from] arrow::error::ArrowError), /// Redis cache errors #[cfg(feature = "redis-cache")] #[error("Redis error: {0}")] Redis(#[from] redis::RedisError), /// Generic errors with transparent forwarding #[error(transparent)] Generic(#[from] anyhow::Error), } // Display implementation is now automatically generated by thiserror // Error trait and From trait implementations are now automatically generated by thiserror impl DataError { /// Create a network error pub fn network>(message: S) -> Self { Self::Network { message: message.into(), } } /// Create a FIX protocol error pub fn fix_protocol>(message: S) -> Self { Self::FixProtocol { message: message.into(), } } /// Create an authentication error pub fn authentication>(message: S) -> Self { Self::Authentication { message: message.into(), } } /// Create a configuration error pub fn configuration, M: Into>(field: F, message: M) -> Self { Self::Configuration { field: field.into(), message: message.into(), } } /// Create a message parsing error pub fn message_parsing>(message: S) -> Self { Self::MessageParsing { message: message.into(), } } /// Create a session error pub fn session>(message: S) -> Self { Self::Session { message: message.into(), } } /// Create an order error pub fn order>(message: S) -> Self { Self::Order { message: message.into(), } } /// Create a timeout error pub fn timeout>(message: S) -> Self { Self::Timeout { message: message.into(), } } /// Create a broker error pub fn broker>(message: S) -> Self { Self::Broker { message: message.into(), } } /// Create a parse error pub fn parse>(message: S) -> Self { Self::Parse { message: message.into(), } } /// Create a validation error pub fn validation, M: Into>(field: F, message: M) -> Self { Self::Validation { field: field.into(), message: message.into(), } } /// Create a serialization error pub fn serialization>(message: S) -> Self { Self::Serialization { message: message.into(), } } /// Create an internal error pub fn internal>(message: S) -> Self { Self::Broker { message: format!("Internal error: {}", message.into()), } } /// Create a compression error pub fn compression>(message: S) -> Self { Self::Compression(message.into()) } /// Create a storage error pub fn storage>(message: S) -> Self { Self::Storage(message.into()) } /// Create an initialization error pub fn initialization>(message: S) -> Self { Self::Initialization(message.into()) } /// Create a simple validation error pub fn validation_simple>(message: S) -> Self { Self::ValidationSimple(message.into()) } /// Create an API error pub fn api, T: Into>(message: S, status: Option) -> Self { Self::Api { message: message.into(), status: status.map(|s| s.into()), } } /// Check if error is retryable pub fn is_retryable(&self) -> bool { match self { Self::Network { .. } => true, Self::Timeout { .. } => true, Self::Connection(_) => true, Self::RateLimit => true, Self::Io(_) => true, Self::Http(_) => true, Self::WebSocket(_) => true, Self::Session { .. } => true, #[cfg(feature = "redis-cache")] Self::Redis(_) => true, _ => false, } } /// Get error severity level pub fn severity(&self) -> ErrorSeverity { match self { Self::Authentication { .. } => ErrorSeverity::Critical, Self::Configuration { .. } => ErrorSeverity::Critical, Self::FixProtocol { .. } => ErrorSeverity::High, Self::Order { .. } => ErrorSeverity::High, Self::Network { .. } => ErrorSeverity::Medium, Self::Session { .. } => ErrorSeverity::Medium, Self::Timeout { .. } => ErrorSeverity::Medium, Self::MessageParsing { .. } => ErrorSeverity::Low, Self::Broker { .. } => ErrorSeverity::Medium, _ => ErrorSeverity::Low, } } /// Get error category for monitoring pub fn category(&self) -> &'static str { match self { Self::Network { .. } => "NETWORK", Self::FixProtocol { .. } => "FIX_PROTOCOL", Self::Authentication { .. } => "AUTHENTICATION", Self::Configuration { .. } => "CONFIGURATION", Self::MessageParsing { .. } => "MESSAGE_PARSING", Self::Session { .. } => "SESSION", Self::Order { .. } => "ORDER", Self::Timeout { .. } => "TIMEOUT", Self::Parse { .. } => "PARSE", Self::Validation { .. } => "VALIDATION", Self::ValidationSimple(_) => "VALIDATION", Self::Serialization { .. } => "SERIALIZATION", Self::Compression(_) => "COMPRESSION", Self::Storage(_) => "STORAGE", Self::NotFound(_) => "NOT_FOUND", Self::Broker { .. } => "BROKER", Self::Connection(_) => "CONNECTION", Self::Api { .. } => "API", Self::InvalidParameter { .. } => "INVALID_PARAMETER", Self::Unsupported(_) => "UNSUPPORTED", Self::NotImplemented(_) => "NOT_IMPLEMENTED", Self::Initialization(_) => "INITIALIZATION", Self::InvalidFormat(_) => "INVALID_FORMAT", Self::Conversion(_) => "CONVERSION", Self::RateLimit => "RATE_LIMIT", Self::Io(_) => "IO", Self::Json(_) => "JSON", Self::Http(_) => "HTTP", Self::WebSocket(_) => "WEBSOCKET", Self::Time(_) => "TIME", Self::Url(_) => "URL", Self::Bincode(_) => "BINCODE", Self::Parquet(_) => "PARQUET", Self::Arrow(_) => "ARROW", #[cfg(feature = "redis-cache")] Self::Redis(_) => "REDIS", Self::Generic(_) => "GENERIC", } } } /// Error severity levels #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] pub enum ErrorSeverity { /// Low severity - informational errors Low, /// Medium severity - recoverable errors Medium, /// High severity - significant errors requiring attention High, /// Critical severity - system-threatening errors Critical, } impl fmt::Display for ErrorSeverity { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Low => write!(f, "LOW"), Self::Medium => write!(f, "MEDIUM"), Self::High => write!(f, "HIGH"), Self::Critical => write!(f, "CRITICAL"), } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_error_creation() { let error = DataError::network("Connection failed"); assert!(matches!(error, DataError::Network { .. })); assert!(error.is_retryable()); assert_eq!(error.severity(), ErrorSeverity::Medium); assert_eq!(error.category(), "NETWORK"); } #[test] fn test_error_severity() { assert_eq!( DataError::authentication("Invalid credentials").severity(), ErrorSeverity::Critical ); assert_eq!( DataError::timeout("Request timeout").severity(), ErrorSeverity::Medium ); } #[test] fn test_retryable_errors() { assert!(DataError::network("test").is_retryable()); assert!(DataError::timeout("test").is_retryable()); assert!(!DataError::authentication("test").is_retryable()); assert!(!DataError::configuration("field", "test").is_retryable()); } #[test] fn test_automatic_from_conversions() { // Test that thiserror automatically converts external errors let io_error = std::io::Error::new(std::io::ErrorKind::NotFound, "test"); let data_error: DataError = io_error.into(); assert!(matches!(data_error, DataError::Io(_))); assert!(data_error.is_retryable()); assert_eq!(data_error.category(), "IO"); let json_error = serde_json::from_str::("invalid").unwrap_err(); let data_error: DataError = json_error.into(); assert!(matches!(data_error, DataError::Json(_))); assert_eq!(data_error.category(), "JSON"); } #[test] fn test_new_error_variants() { let compression_error = DataError::compression("Failed to compress"); assert!(matches!(compression_error, DataError::Compression(_))); assert_eq!(compression_error.category(), "COMPRESSION"); let storage_error = DataError::storage("Storage failed"); assert!(matches!(storage_error, DataError::Storage(_))); assert_eq!(storage_error.category(), "STORAGE"); let rate_limit_error = DataError::RateLimit; assert!(matches!(rate_limit_error, DataError::RateLimit)); assert!(rate_limit_error.is_retryable()); assert_eq!(rate_limit_error.category(), "RATE_LIMIT"); } #[test] fn test_consolidated_variants() { // Test that consolidated variants work properly let validation_error = DataError::validation("field", "invalid"); assert!(matches!(validation_error, DataError::Validation { .. })); assert_eq!(validation_error.category(), "VALIDATION"); let simple_validation_error = DataError::validation_simple("invalid"); assert!(matches!(simple_validation_error, DataError::ValidationSimple(_))); assert_eq!(simple_validation_error.category(), "VALIDATION"); let api_error = DataError::api("API failed", Some("404")); assert!(matches!(api_error, DataError::Api { .. })); assert_eq!(api_error.category(), "API"); } }