- Fixed systematic array indexing corruption: [0_i32] → [0] - Fixed numeric literal suffixes across 835 files - Fixed iterator patterns on RwLockReadGuard (.iter() required) - Fixed float type annotations (365.25_f64 for sqrt) - Fixed missing semicolons in position manager - Fixed reference dereferencing in data loader Root cause: Mass refactoring incorrectly added _i32 suffixes to array indices Impact: Complete compilation failure (463 errors) Resolution: Automated regex + targeted fixes Result: 100% compilation success (0 errors) Validated: cargo check --workspace passes Ready for: Production deployment
288 lines
10 KiB
Rust
288 lines
10 KiB
Rust
//! 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<T> = common::error::CommonResult<T>;
|
|
|
|
/// 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<std::io::Error> for DataServiceError {
|
|
fn from(err: std::io::Error) -> Self {
|
|
DataServiceError::Common(CommonError::network(format!("IO error: {}", err)))
|
|
}
|
|
}
|
|
|
|
impl From<serde_json::Error> for DataServiceError {
|
|
fn from(err: serde_json::Error) -> Self {
|
|
DataServiceError::Common(CommonError::serialization(format!("JSON error: {}", err)))
|
|
}
|
|
}
|
|
|
|
impl From<reqwest::Error> for DataServiceError {
|
|
fn from(err: reqwest::Error) -> Self {
|
|
DataServiceError::Common(CommonError::network(format!("HTTP error: {}", err)))
|
|
}
|
|
}
|
|
|
|
impl From<tokio_tungstenite::tungstenite::Error> for DataServiceError {
|
|
fn from(err: tokio_tungstenite::tungstenite::Error) -> Self {
|
|
DataServiceError::Common(CommonError::connection("websocket", format!("{}", err)))
|
|
}
|
|
}
|
|
|
|
impl From<chrono::ParseError> for DataServiceError {
|
|
fn from(err: chrono::ParseError) -> Self {
|
|
DataServiceError::Common(CommonError::validation("timestamp", format!("Parse error: {}", err)))
|
|
}
|
|
}
|
|
|
|
impl From<url::ParseError> for DataServiceError {
|
|
fn from(err: url::ParseError) -> Self {
|
|
DataServiceError::Common(CommonError::validation("url", format!("URL parse error: {}", err)))
|
|
}
|
|
}
|
|
|
|
impl From<anyhow::Error> 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<S: Into<String>, M: Into<String>>(session_id: S, message: M) -> Self {
|
|
Self::FixProtocol {
|
|
session_id: session_id.into(),
|
|
message: message.into(),
|
|
}
|
|
}
|
|
|
|
/// Create broker connection error
|
|
pub fn broker_connection<B: Into<String>, M: Into<String>>(broker: B, message: M) -> Self {
|
|
Self::BrokerConnection {
|
|
broker: broker.into(),
|
|
message: message.into(),
|
|
}
|
|
}
|
|
|
|
/// Create market data provider error
|
|
pub fn market_data_provider<P: Into<String>, S: Into<String>, M: Into<String>>(
|
|
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<M: Into<String>>(message: M) -> Self {
|
|
Self::Common(CommonError::network(message))
|
|
}
|
|
|
|
/// Create authentication error using CommonError
|
|
pub fn authentication<M: Into<String>>(message: M) -> Self {
|
|
Self::Common(CommonError::authentication(message))
|
|
}
|
|
|
|
/// Create configuration error using CommonError
|
|
pub fn configuration<M: Into<String>>(message: M) -> Self {
|
|
Self::Common(CommonError::config(message))
|
|
}
|
|
|
|
/// Create validation error using CommonError
|
|
pub fn validation<F: Into<String>, M: Into<String>>(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<M: Into<String>>(message: M) -> Self {
|
|
Self::Common(CommonError::serialization(message))
|
|
}
|
|
|
|
/// Create internal error using CommonError
|
|
pub fn internal<M: Into<String>>(message: M) -> Self {
|
|
Self::Common(CommonError::internal(message))
|
|
}
|
|
|
|
/// Create not found error using CommonError
|
|
pub fn not_found<R: Into<String>, I: Into<String>>(resource: R, identifier: I) -> Self {
|
|
Self::Common(CommonError::not_found(resource, identifier))
|
|
}
|
|
|
|
/// Create trading error using CommonError
|
|
pub fn trading<M: Into<String>>(message: M) -> Self {
|
|
Self::Common(CommonError::trading(message))
|
|
}
|
|
}
|
|
|
|
/// Convert to CommonError automatically for interop
|
|
impl From<DataServiceError> 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);
|
|
}
|
|
} |