//! Consolidated error handling for the data module using CommonError //! //! This module demonstrates the consolidated error handling pattern //! using the common error system across all Foxhunt services. // REMOVED: All pub use statements eliminated per cleanup requirements // Use direct import: common::error::{CommonError, CommonResult, ErrorCategory, RetryStrategy, ErrorSeverity} /// Result type for data module operations using CommonError pub type DataResult = common::error::CommonResult; /// Data module specific error extensions /// For cases where we need domain-specific error information beyond CommonError #[derive(Debug, thiserror::Error)] pub enum DataServiceError { /// Common error with context #[error("Data service error: {0}")] Common(#[from] common::error::CommonError), /// `FIX` protocol specific error with detailed context #[error("FIX protocol error: {session_id} - {message}")] FixProtocol { session_id: String, message: String, }, /// Broker connection with specific broker context #[error("Broker connection error: {broker} - {message}")] BrokerConnection { broker: String, message: String, }, /// Market data provider specific error #[error("Market data provider error: {provider} - {symbol} - {message}")] MarketDataProvider { provider: String, symbol: String, message: String, }, } impl DataServiceError { /// Convert to CommonError for metrics and monitoring pub fn to_common_error(self) -> CommonError { match self { DataServiceError::Common(err) => err, DataServiceError::FixProtocol { session_id, message } => { CommonError::service( ErrorCategory::Trading, format!("FIX protocol error [{}]: {}", session_id, message) ) } DataServiceError::BrokerConnection { broker, message } => { CommonError::connection(broker, message) } DataServiceError::MarketDataProvider { provider, symbol, message } => { CommonError::service( ErrorCategory::MarketData, format!("Provider {} symbol {}: {}", provider, symbol, message) ) } } } /// Get error category for metrics pub fn category(&self) -> ErrorCategory { self.to_common_error().category() } /// Get error severity pub fn severity(&self) -> ErrorSeverity { self.to_common_error().severity() } /// Get retry strategy pub fn retry_strategy(&self) -> RetryStrategy { self.to_common_error().retry_strategy() } /// Check if error is retryable pub fn is_retryable(&self) -> bool { self.to_common_error().is_retryable() } /// Get error code for monitoring pub fn error_code(&self) -> &'static str { match self { DataServiceError::Common(_) => "DATA_COMMON_ERROR", DataServiceError::FixProtocol { .. } => "DATA_FIX_PROTOCOL_ERROR", DataServiceError::BrokerConnection { .. } => "DATA_BROKER_CONNECTION_ERROR", DataServiceError::MarketDataProvider { .. } => "DATA_MARKET_DATA_PROVIDER_ERROR", } } } /// Convert standard errors to CommonError for consistent handling impl From for DataServiceError { fn from(err: std::io::Error) -> Self { DataServiceError::Common(CommonError::network(format!("IO error: {}", err))) } } impl From for DataServiceError { fn from(err: serde_json::Error) -> Self { DataServiceError::Common(CommonError::serialization(format!("JSON error: {}", err))) } } impl From for DataServiceError { fn from(err: reqwest::Error) -> Self { DataServiceError::Common(CommonError::network(format!("HTTP error: {}", err))) } } impl From for DataServiceError { fn from(err: tokio_tungstenite::tungstenite::Error) -> Self { DataServiceError::Common(CommonError::connection("websocket", format!("{}", err))) } } impl From for DataServiceError { fn from(err: chrono::ParseError) -> Self { DataServiceError::Common(CommonError::validation("timestamp", format!("Parse error: {}", err))) } } impl From for DataServiceError { fn from(err: url::ParseError) -> Self { DataServiceError::Common(CommonError::validation("url", format!("URL parse error: {}", err))) } } impl From for DataServiceError { fn from(err: anyhow::Error) -> Self { DataServiceError::Common(CommonError::internal(format!("Anyhow error: {}", err))) } } /// Convenience functions for creating data service errors impl DataServiceError { /// Create `FIX` protocol error pub fn fix_protocol, M: Into>(session_id: S, message: M) -> Self { Self::FixProtocol { session_id: session_id.into(), message: message.into(), } } /// Create broker connection error pub fn broker_connection, M: Into>(broker: B, message: M) -> Self { Self::BrokerConnection { broker: broker.into(), message: message.into(), } } /// Create market data provider error pub fn market_data_provider, S: Into, M: Into>( provider: P, symbol: S, message: M, ) -> Self { Self::MarketDataProvider { provider: provider.into(), symbol: symbol.into(), message: message.into(), } } /// Create network error using CommonError pub fn network>(message: M) -> Self { Self::Common(CommonError::network(message)) } /// Create authentication error using CommonError pub fn authentication>(message: M) -> Self { Self::Common(CommonError::authentication(message)) } /// Create configuration error using CommonError pub fn configuration>(message: M) -> Self { Self::Common(CommonError::config(message)) } /// Create validation error using CommonError pub fn validation, M: Into>(field: F, message: M) -> Self { Self::Common(CommonError::validation(field, message)) } /// Create timeout error using CommonError pub fn timeout(actual_ms: u64, max_ms: u64) -> Self { Self::Common(CommonError::timeout(actual_ms, max_ms)) } /// Create serialization error using CommonError pub fn serialization>(message: M) -> Self { Self::Common(CommonError::serialization(message)) } /// Create internal error using CommonError pub fn internal>(message: M) -> Self { Self::Common(CommonError::internal(message)) } /// Create not found error using CommonError pub fn not_found, I: Into>(resource: R, identifier: I) -> Self { Self::Common(CommonError::not_found(resource, identifier)) } /// Create trading error using CommonError pub fn trading>(message: M) -> Self { Self::Common(CommonError::trading(message)) } } /// Convert to CommonError automatically for interop impl From for CommonError { fn from(err: DataServiceError) -> Self { err.to_common_error() } } #[cfg(test)] mod tests { use super::*; #[test] fn test_data_service_error_categorization() { let fix_error = DataServiceError::fix_protocol("SESSION_001", "Heartbeat timeout"); assert_eq!(fix_error.category(), ErrorCategory::Trading); assert_eq!(fix_error.error_code(), "DATA_FIX_PROTOCOL_ERROR"); let provider_error = DataServiceError::market_data_provider("DATABENTO", "AAPL", "Connection lost"); assert_eq!(provider_error.category(), ErrorCategory::MarketData); assert!(provider_error.is_retryable()); } #[test] fn test_common_error_integration() { let network_error = DataServiceError::network("Connection refused"); let common_error: CommonError = network_error.into(); assert_eq!(common_error.category(), ErrorCategory::Network); assert!(common_error.is_retryable()); match common_error.retry_strategy() { RetryStrategy::Exponential { .. } => (), _ => panic!("Expected exponential backoff for network errors"), } } #[test] fn test_error_conversion_chain() { let io_error = std::io::Error::new(std::io::ErrorKind::ConnectionRefused, "Connection refused"); let data_error: DataServiceError = io_error.into(); let common_error: CommonError = data_error.into(); assert_eq!(common_error.category(), ErrorCategory::Network); assert_eq!(common_error.severity(), ErrorSeverity::Warn); } #[test] fn test_retry_strategies() { let auth_error = DataServiceError::authentication("Invalid token"); assert!(!auth_error.is_retryable()); assert_eq!(auth_error.retry_strategy(), RetryStrategy::NoRetry); let timeout_error = DataServiceError::timeout(5000, 2000); assert!(timeout_error.is_retryable()); match timeout_error.retry_strategy() { RetryStrategy::Linear { .. } => (), _ => panic!("Expected linear backoff for timeout errors"), } } #[test] fn test_error_severity_classification() { let config_error = DataServiceError::configuration("Missing API key"); assert_eq!(config_error.severity(), ErrorSeverity::Critical); let validation_error = DataServiceError::validation("price", "Must be positive"); assert_eq!(validation_error.severity(), ErrorSeverity::Info); let broker_error = DataServiceError::broker_connection("INTERACTIVE_BROKERS", "Connection lost"); assert_eq!(broker_error.severity(), ErrorSeverity::Warn); } }