//! 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 { /// Error message message: String, }, /// `FIX` protocol errors #[error("FIX protocol error: {message}")] FixProtocol { /// Error message message: String, }, /// Authentication errors #[error("Authentication error: {message}")] Authentication { /// Error message message: String, }, /// Configuration errors #[error("Configuration error in field '{field}': {message}")] Configuration { /// Field name with configuration error field: String, /// Error message message: String, }, /// Message parsing errors #[error("Message parsing error: {message}")] MessageParsing { /// Error message message: String, }, /// Session management errors #[error("Session error: {message}")] Session { /// Error message message: String, }, /// Order management errors #[error("Order error: {message}")] Order { /// Error message message: String, }, /// Timeout errors #[error("Operation timed out: {message}")] Timeout { /// Error message message: String, }, /// Parse errors #[error("Parse error: {message}")] Parse { /// Error message message: String, }, /// Validation errors (consolidated) #[error("Validation error in field '{field}': {message}")] Validation { /// Field name with validation error field: String, /// Error message message: String, }, /// Simple validation error #[error("Validation error: {0}")] ValidationSimple(String), /// Serialization errors (consolidated) #[error("Serialization error: {message}")] Serialization { /// Error message 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 { /// Error message message: String, }, /// Connection errors #[error("Connection error: {0}")] Connection(String), /// Subscription errors #[error("Subscription error: {message}")] Subscription { /// Error message message: String, }, /// API errors #[error("API error: {message} (status: {status:?})")] Api { /// Error message message: String, /// HTTP status code if available status: Option, }, /// Invalid parameter errors #[error("Invalid parameter '{field}': {message}")] InvalidParameter { /// Parameter name field: String, /// Error message 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] 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), /// Trading engine errors #[error("Trading engine error: {0}")] TradingEngine(#[from] common::CommonTypeError), /// Configuration module errors #[error("Config error: {0}")] ConfigError(#[from] config::error::ConfigError), } // 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 a subscription error pub fn subscription>(message: S) -> Self { Self::Subscription { message: 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, Self::Storage(_) => true, // Storage errors may be transient #[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::NotFound(_) => 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 { .. } => "PARSING", 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::Subscription { .. } => "SUBSCRIPTION", 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", Self::TradingEngine(_) => "TRADING_ENGINE", Self::ConfigError(_) => "CONFIG", } } } /// 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"); } #[test] fn test_error_categories() { assert_eq!(DataError::network("test").category(), "NETWORK"); assert_eq!(DataError::timeout("test").category(), "TIMEOUT"); assert_eq!(DataError::parse("test").category(), "PARSING"); assert_eq!(DataError::RateLimit.category(), "RATE_LIMIT"); assert_eq!( DataError::authentication("test").category(), "AUTHENTICATION" ); assert_eq!( DataError::NotFound("test".to_string()).category(), "NOT_FOUND" ); } #[test] fn test_error_display() { let error = DataError::network("Connection refused"); let display = format!("{}", error); assert!(display.contains("Network")); let error = DataError::validation("price", "must be positive"); let display = format!("{}", error); assert!(display.contains("price")); assert!(display.contains("must be positive")); } #[test] fn test_non_retryable_errors() { assert!(!DataError::parse("test").is_retryable()); assert!(!DataError::validation("field", "test").is_retryable()); assert!(!DataError::ValidationSimple("test".to_string()).is_retryable()); assert!(!DataError::NotFound("test".to_string()).is_retryable()); } #[test] fn test_severity_levels() { assert_eq!(DataError::network("test").severity(), ErrorSeverity::Medium); assert_eq!(DataError::timeout("test").severity(), ErrorSeverity::Medium); assert_eq!( DataError::authentication("test").severity(), ErrorSeverity::Critical ); assert_eq!(DataError::RateLimit.severity(), ErrorSeverity::Low); assert_eq!(DataError::parse("test").severity(), ErrorSeverity::Low); } #[test] fn test_severity_display() { assert_eq!(format!("{}", ErrorSeverity::Low), "LOW"); assert_eq!(format!("{}", ErrorSeverity::Medium), "MEDIUM"); assert_eq!(format!("{}", ErrorSeverity::High), "HIGH"); assert_eq!(format!("{}", ErrorSeverity::Critical), "CRITICAL"); } #[test] fn test_error_with_context() { let error = DataError::network("Connection failed"); let error_string = format!("{}", error); assert!(error_string.contains("Network")); } #[test] fn test_not_found_error() { let error = DataError::NotFound("dataset123".to_string()); assert!(!error.is_retryable()); assert_eq!(error.severity(), ErrorSeverity::Medium); assert_eq!(error.category(), "NOT_FOUND"); } #[test] fn test_compression_error() { let error = DataError::compression("ZSTD compression failed"); assert!(matches!(error, DataError::Compression(_))); assert!(!error.is_retryable()); assert_eq!(error.category(), "COMPRESSION"); } #[test] fn test_storage_error() { let error = DataError::storage("Disk full"); assert!(matches!(error, DataError::Storage(_))); assert!(error.is_retryable()); assert_eq!(error.category(), "STORAGE"); } #[test] fn test_timeout_error() { let error = DataError::timeout("Request timeout after 30s"); assert!(error.is_retryable()); assert_eq!(error.severity(), ErrorSeverity::Medium); } #[test] fn test_configuration_error() { let error = DataError::configuration("api_key", "Missing required field"); assert!(!error.is_retryable()); assert_eq!(error.severity(), ErrorSeverity::Critical); } #[test] fn test_api_error_with_code() { let error = DataError::api("Unauthorized", Some("401")); assert!(matches!(error, DataError::Api { .. })); assert_eq!(error.category(), "API"); } #[test] fn test_api_error_without_code() { let error = DataError::api("Server error", None::); assert!(matches!(error, DataError::Api { .. })); assert_eq!(error.category(), "API"); } #[test] fn test_websocket_error() { let ws_error = tungstenite::Error::ConnectionClosed; let error = DataError::WebSocket(ws_error); assert!(error.is_retryable()); assert_eq!(error.category(), "WEBSOCKET"); } }