Files
foxhunt/common/src/error.rs
jgrusewski 030a15ee05 🔧 Emergency Fix: Resolve catastrophic _i32 suffix corruption (463→0 errors)
- 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
2025-10-10 23:05:26 +02:00

365 lines
12 KiB
Rust

//! Common error types and utilities
//!
//! This module provides shared error types and utilities used across
//! all Foxhunt services.
use serde::{Deserialize, Serialize};
use std::fmt;
use std::time::Duration;
use thiserror::Error;
/// Common error type for all Foxhunt services
#[derive(Debug, Error)]
pub enum CommonError {
/// Database operation failed - wraps database-specific errors
#[error("Database error: {0}")]
Database(#[from] crate::database::DatabaseError),
/// Configuration is invalid or missing required parameters
#[error("Configuration error: {0}")]
Configuration(String),
/// Network communication error occurred
#[error("Network error: {0}")]
Network(String),
/// Service-specific error with categorization for metrics
#[error("Service error: {category} - {message}")]
Service {
/// Error category for classification
category: ErrorCategory,
/// Descriptive error message
message: String,
},
/// Input validation failed
#[error("Validation error: {0}")]
Validation(String),
/// Operation exceeded maximum allowed execution time
#[error("Timeout error: operation took {actual_ms}ms, max allowed {max_ms}ms")]
Timeout {
/// Actual execution time in milliseconds
actual_ms: u64,
/// Maximum allowed execution time in milliseconds
max_ms: u64,
},
}
/// Error categories for classification and metrics
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[allow(clippy::module_name_repetitions)]
pub enum ErrorCategory {
/// Market data related errors
MarketData,
/// Trading and order management errors
Trading,
/// Network and communication errors
Network,
/// System and infrastructure errors
System,
/// Configuration errors
Configuration,
/// Validation errors
Validation,
/// Critical errors requiring immediate attention
Critical,
/// Connection errors (data providers)
Connection,
/// Authentication errors
Authentication,
/// Rate limiting errors
RateLimit,
/// Data parsing errors
Parse,
/// Subscription errors
Subscription,
/// Financial safety and calculation errors
FinancialSafety,
/// Risk management and circuit breakers
RiskManagement,
/// Database and persistence layer
Database,
/// Broker connectivity and execution
Broker,
/// Machine learning and AI errors
MachineLearning,
/// Security and authentication errors
Security,
/// Business logic errors
BusinessLogic,
/// Resource errors (not found, conflicts)
Resource,
/// Development and testing errors
Development,
/// Risk management errors
Risk,
/// Machine learning errors (alias for `MachineLearning`)
ML,
/// Unknown/other errors
Other,
}
impl fmt::Display for ErrorCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MarketData => write!(f, "MARKET_DATA"),
Self::Trading => write!(f, "TRADING"),
Self::Network => write!(f, "NETWORK"),
Self::System => write!(f, "SYSTEM"),
Self::Configuration => write!(f, "CONFIGURATION"),
Self::Validation => write!(f, "VALIDATION"),
Self::Critical => write!(f, "CRITICAL"),
Self::Connection => write!(f, "CONNECTION"),
Self::Authentication => write!(f, "AUTHENTICATION"),
Self::RateLimit => write!(f, "RATE_LIMIT"),
Self::Parse => write!(f, "PARSE"),
Self::Subscription => write!(f, "SUBSCRIPTION"),
Self::FinancialSafety => write!(f, "FINANCIAL_SAFETY"),
Self::RiskManagement => write!(f, "RISK_MANAGEMENT"),
Self::Database => write!(f, "DATABASE"),
Self::Broker => write!(f, "BROKER"),
Self::MachineLearning => write!(f, "MACHINE_LEARNING"),
Self::Security => write!(f, "SECURITY"),
Self::BusinessLogic => write!(f, "BUSINESS_LOGIC"),
Self::Resource => write!(f, "RESOURCE"),
Self::Development => write!(f, "DEVELOPMENT"),
Self::Risk => write!(f, "RISK"),
Self::ML => write!(f, "ML"),
Self::Other => write!(f, "OTHER"),
}
}
}
/// Error severity levels for prioritization and alerting
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[allow(clippy::module_name_repetitions)]
pub enum ErrorSeverity {
/// Debug level - for development and troubleshooting
Debug,
/// Info level - informational messages
Info,
/// Warning level - potentially problematic situations
Warn,
/// Error level - error conditions that should be addressed
Error,
/// Critical level - serious error conditions requiring immediate attention
Critical,
}
impl fmt::Display for ErrorSeverity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Debug => write!(f, "DEBUG"),
Self::Info => write!(f, "INFO"),
Self::Warn => write!(f, "WARN"),
Self::Error => write!(f, "ERROR"),
Self::Critical => write!(f, "CRITICAL"),
}
}
}
/// Retry strategies for error recovery
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RetryStrategy {
/// Do not retry - error is permanent
NoRetry,
/// Retry immediately without delay
Immediate,
/// Linear backoff with fixed intervals
Linear {
/// Base delay in milliseconds between retries
base_delay_ms: u64,
},
/// Exponential backoff with jitter
Exponential {
/// Base delay in milliseconds for exponential backoff
base_delay_ms: u64,
/// Maximum delay cap in milliseconds
max_delay_ms: u64,
},
/// Wait for circuit breaker to close
CircuitBreaker,
}
impl RetryStrategy {
/// Calculate delay for retry attempt
#[must_use]
pub fn calculate_delay(&self, attempt: u32) -> Option<Duration> {
match self {
Self::NoRetry => None,
Self::Immediate => Some(Duration::from_millis(0)),
Self::Linear { base_delay_ms } => {
Some(Duration::from_millis(base_delay_ms.saturating_mul(u64::from(attempt))))
},
Self::Exponential {
base_delay_ms,
max_delay_ms,
} => {
let delay_ms = base_delay_ms.saturating_mul(2_u64.saturating_pow(attempt.min(10)));
let capped_delay = delay_ms.min(*max_delay_ms);
// Add simple jitter (±10%)
#[allow(clippy::integer_division)]
let jitter_ms = capped_delay / 10;
#[allow(clippy::integer_division)]
let final_delay = capped_delay.saturating_sub(jitter_ms / 2);
Some(Duration::from_millis(final_delay))
},
Self::CircuitBreaker => Some(Duration::from_secs(30)),
}
}
/// Get maximum recommended retry attempts
#[must_use]
pub const fn max_attempts(&self) -> Option<u32> {
match self {
Self::NoRetry => Some(0),
Self::Immediate => Some(3),
Self::Linear { .. } => Some(5),
Self::Exponential { .. } => Some(7),
Self::CircuitBreaker => Some(1),
}
}
}
/// Convenience functions for creating common errors
impl CommonError {
/// Create a configuration error
pub fn config<S: Into<String>>(message: S) -> Self {
Self::Configuration(message.into())
}
/// Create a network error
pub fn network<S: Into<String>>(message: S) -> Self {
Self::Network(message.into())
}
/// Create a service error with category
pub fn service<S: Into<String>>(category: ErrorCategory, message: S) -> Self {
Self::Service {
category,
message: message.into(),
}
}
/// Create a validation error
pub fn validation<S: Into<String>>(message: S) -> Self {
Self::Validation(message.into())
}
/// Create a timeout error
pub const fn timeout(actual_ms: u64, max_ms: u64) -> Self {
Self::Timeout { actual_ms, max_ms }
}
/// Create a machine learning specific service error
pub fn ml<S: Into<String>, M: Into<String>>(model_name: S, message: M) -> Self {
Self::Service {
category: ErrorCategory::MachineLearning,
message: format!("{}: {}", model_name.into(), message.into()),
}
}
/// Create a serialization error
pub fn serialization<S: Into<String>>(message: S) -> Self {
Self::Service {
category: ErrorCategory::Parse,
message: format!("Serialization error: {}", message.into()),
}
}
/// Create an internal error
pub fn internal<S: Into<String>>(message: S) -> Self {
Self::Service {
category: ErrorCategory::System,
message: format!("Internal error: {}", message.into()),
}
}
/// Create a resource exhausted error
pub fn resource_exhausted<S: Into<String>>(resource: S) -> Self {
Self::Service {
category: ErrorCategory::Resource,
message: format!("Resource exhausted: {}", resource.into()),
}
}
/// Get the error category for classification and metrics
pub const fn category(&self) -> ErrorCategory {
match self {
Self::Database(_) => ErrorCategory::Database,
Self::Configuration(_) => ErrorCategory::Configuration,
Self::Network(_) => ErrorCategory::Network,
Self::Service { category, .. } => *category,
Self::Validation(_) => ErrorCategory::Validation,
Self::Timeout { .. } => ErrorCategory::System,
}
}
/// Get error severity level
pub const fn severity(&self) -> ErrorSeverity {
match self {
Self::Database(_) => ErrorSeverity::Critical,
Self::Configuration(_) => ErrorSeverity::Critical,
Self::Network(_) => ErrorSeverity::Error,
Self::Service { category, .. } => match category {
ErrorCategory::Critical
| ErrorCategory::FinancialSafety
| ErrorCategory::Authentication => ErrorSeverity::Critical,
ErrorCategory::Trading
| ErrorCategory::RiskManagement
| ErrorCategory::Database => ErrorSeverity::Error,
_ => ErrorSeverity::Warn,
},
Self::Validation(_) => ErrorSeverity::Warn,
Self::Timeout { .. } => ErrorSeverity::Error,
}
}
/// Check if the error is retryable
pub const fn is_retryable(&self) -> bool {
match self {
Self::Database(_) => true, // Database operations can be retried
Self::Configuration(_) => false, // Configuration errors are permanent
Self::Network(_) => true, // Network errors are often transient
Self::Service { category, .. } => !matches!(
category,
ErrorCategory::Authentication
| ErrorCategory::Configuration
| ErrorCategory::Validation
),
Self::Validation(_) => false, // Validation errors are permanent
Self::Timeout { .. } => true, // Timeouts can be retried
}
}
/// Get retry strategy for this error
pub const fn retry_strategy(&self) -> RetryStrategy {
if !self.is_retryable() {
return RetryStrategy::NoRetry;
}
match self {
Self::Database(_) => RetryStrategy::Exponential {
base_delay_ms: 1000,
max_delay_ms: 10000,
},
Self::Network(_) => RetryStrategy::Linear { base_delay_ms: 500 },
Self::Service { category, .. } => match category {
ErrorCategory::Network | ErrorCategory::Connection => {
RetryStrategy::Linear { base_delay_ms: 500 }
},
ErrorCategory::RateLimit => RetryStrategy::Exponential {
base_delay_ms: 5000,
max_delay_ms: 60000,
},
_ => RetryStrategy::Immediate,
},
Self::Timeout { .. } => RetryStrategy::Linear {
base_delay_ms: 1000,
},
_ => RetryStrategy::NoRetry,
}
}
}
/// Result type for common operations
pub type CommonResult<T> = Result<T, CommonError>;