Files
foxhunt/common/src/error/service_error.rs
jgrusewski 2df1ea92e1 feat(ml): WAVE 29 DQN Codebase Cleanup & Refactoring Campaign
BREAKING CHANGES:
- Removed orphaned dqn.rs monolithic trainer (4,975 lines)
- Removed orphaned dqn_ensemble.rs module (816 lines)
- Removed orphaned tft.rs and tft_complete_int8_integration_test.rs
- TFT trainer split into modular directory structure

DQN Module Refactoring:
- Split trainers/dqn.rs into modular structure (config.rs, statistics.rs, trainer.rs)
- Fixed hyperopt 39D search space (continuous params only)
- Boolean flags (use_dueling, use_double_dqn, use_per, use_noisy_nets) are now FIXED architectural decisions
- use_distributional defaults to false (Candle BUG #36 - scatter_add gradient issues)

Clean Module Structure:
- ml/src/trainers/dqn/ directory with proper mod.rs exports
- ml/src/trainers/tft/ directory with config.rs, types.rs, model.rs, trainer.rs, tests.rs
- All P0 features validated: TD-error clamping, batch diversity, LR scheduler, priority staleness

Documentation:
- Added comprehensive docs in docs/codebase-cleanup/
- ADR-001 for DQN refactoring decisions
- Rainbow DQN component matrix and quick reference guides

Build Status: Compiles with zero errors

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-27 23:46:13 +01:00

202 lines
6.4 KiB
Rust

//! Service error trait for standardized error handling across all services
//!
//! This module provides a common trait that all service-specific errors implement,
//! enabling consistent error handling, metrics, and retry logic across the codebase.
use super::{CommonError, ErrorCategory, ErrorSeverity, RetryStrategy};
/// Common trait for service-specific error types
///
/// Implement this trait to provide standardized error handling for your service.
/// This enables consistent metrics collection, retry logic, and error categorization.
///
/// # Example
///
/// ```rust
/// use common::error::{ServiceErrorExt, CommonError, ErrorCategory, ErrorSeverity, RetryStrategy};
///
/// #[derive(Debug, thiserror::Error)]
/// pub enum MyServiceError {
/// #[error("Common error: {0}")]
/// Common(#[from] CommonError),
///
/// #[error("Custom error: {0}")]
/// Custom(String),
/// }
///
/// impl ServiceErrorExt for MyServiceError {
/// fn error_code(&self) -> &'static str {
/// match self {
/// Self::Common(_) => "MY_COMMON_ERROR",
/// Self::Custom(_) => "MY_CUSTOM_ERROR",
/// }
/// }
///
/// fn category(&self) -> ErrorCategory {
/// match self {
/// Self::Common(err) => err.category(),
/// Self::Custom(_) => ErrorCategory::System,
/// }
/// }
///
/// fn severity(&self) -> ErrorSeverity {
/// match self {
/// Self::Common(err) => err.severity(),
/// Self::Custom(_) => ErrorSeverity::Error,
/// }
/// }
///
/// fn retry_strategy(&self) -> RetryStrategy {
/// match self {
/// Self::Common(err) => err.retry_strategy(),
/// Self::Custom(_) => RetryStrategy::Linear { base_delay_ms: 1000 },
/// }
/// }
///
/// fn to_common_error(self) -> CommonError {
/// match self {
/// Self::Common(err) => err,
/// Self::Custom(msg) => CommonError::internal(msg),
/// }
/// }
/// }
/// ```
pub trait ServiceErrorExt: std::error::Error + Send + Sync {
/// Get unique error code for monitoring and alerting
///
/// Error codes should be:
/// - Uppercase with underscores (e.g., "ML_MODEL_TRAINING_ERROR")
/// - Service-prefixed (e.g., "ML_", "RISK_", "DATA_", "TLI_")
/// - Descriptive and unique within the service
fn error_code(&self) -> &'static str;
/// Get error category for classification and metrics
fn category(&self) -> ErrorCategory;
/// Get error severity level for prioritization and alerting
fn severity(&self) -> ErrorSeverity;
/// Get retry strategy for automatic error recovery
fn retry_strategy(&self) -> RetryStrategy;
/// Convert service-specific error to CommonError for interoperability
///
/// This enables seamless integration with common error handling infrastructure
/// and allows service errors to be used in generic contexts.
fn to_common_error(self) -> CommonError;
/// Check if error is retryable based on retry strategy
fn is_retryable(&self) -> bool {
!matches!(self.retry_strategy(), RetryStrategy::NoRetry)
}
}
/// Extension trait for Result types using service errors
pub trait ServiceResultExt<T, E: ServiceErrorExt> {
/// Map service error to CommonError
fn map_to_common(self) -> Result<T, CommonError>;
/// Extract error code if Result is Err
fn error_code(&self) -> Option<&'static str>;
/// Check if error is retryable
fn is_retryable(&self) -> bool;
}
impl<T, E: ServiceErrorExt> ServiceResultExt<T, E> for Result<T, E> {
fn map_to_common(self) -> Result<T, CommonError> {
self.map_err(ServiceErrorExt::to_common_error)
}
fn error_code(&self) -> Option<&'static str> {
self.as_ref().err().map(ServiceErrorExt::error_code)
}
fn is_retryable(&self) -> bool {
self.as_ref().err().is_some_and(ServiceErrorExt::is_retryable)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, thiserror::Error)]
enum TestServiceError {
#[error("Common error: {0}")]
Common(#[from] CommonError),
#[error("Test error: {0}")]
Test(String),
}
impl ServiceErrorExt for TestServiceError {
fn error_code(&self) -> &'static str {
match self {
Self::Common(_) => "TEST_COMMON_ERROR",
Self::Test(_) => "TEST_CUSTOM_ERROR",
}
}
fn category(&self) -> ErrorCategory {
match self {
Self::Common(err) => err.category(),
Self::Test(_) => ErrorCategory::System,
}
}
fn severity(&self) -> ErrorSeverity {
match self {
Self::Common(err) => err.severity(),
Self::Test(_) => ErrorSeverity::Error,
}
}
fn retry_strategy(&self) -> RetryStrategy {
match self {
Self::Common(err) => err.retry_strategy(),
Self::Test(_) => RetryStrategy::NoRetry,
}
}
fn to_common_error(self) -> CommonError {
match self {
Self::Common(err) => err,
Self::Test(msg) => CommonError::internal(msg),
}
}
}
#[test]
fn test_service_error_trait() {
let error = TestServiceError::Test("test error".to_string());
assert_eq!(error.error_code(), "TEST_CUSTOM_ERROR");
assert_eq!(error.category(), ErrorCategory::System);
assert_eq!(error.severity(), ErrorSeverity::Error);
assert!(!error.is_retryable());
}
#[test]
fn test_common_error_delegation() {
let common_err = CommonError::config("test config error");
let error = TestServiceError::Common(common_err);
assert_eq!(error.error_code(), "TEST_COMMON_ERROR");
assert_eq!(error.category(), ErrorCategory::Configuration);
assert_eq!(error.severity(), ErrorSeverity::Critical);
assert!(!error.is_retryable());
}
#[test]
fn test_result_extensions() {
let ok_result: Result<i32, TestServiceError> = Ok(42);
assert!(ok_result.error_code().is_none());
assert!(!ok_result.is_retryable());
let err_result: Result<i32, TestServiceError> = Err(TestServiceError::Test("error".to_string()));
assert_eq!(err_result.error_code(), Some("TEST_CUSTOM_ERROR"));
assert!(!err_result.is_retryable());
}
}