use core::result::Result as CoreResult;
use std::collections::HashMap;
use std::time::Duration;
use super::Error;
#[derive(Debug, Clone, PartialEq)]
pub enum RecoveryStrategy {
Fail,
LogAndContinue(LogLevel),
UseDefault(String),
Retry {
max_attempts: usize,
backoff_ms: u64,
},
Graceful(String),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum LogLevel {
Debug,
Info,
Warn,
Error,
}
#[derive(Debug)]
pub enum RecoveryResult<T> {
Success(T),
Failed(Error),
Recovered {
value: Option<T>,
reason: String,
},
Retrying {
attempt: usize,
next_backoff: Duration,
},
}
#[derive(Debug, Clone)]
pub struct ErrorRecoveryManager {
strategies: HashMap<String, RecoveryStrategy>,
default_strategy: RecoveryStrategy,
}
impl Default for ErrorRecoveryManager {
fn default() -> Self {
Self::new()
}
}
impl ErrorRecoveryManager {
pub fn new() -> Self {
Self { strategies: HashMap::new(), default_strategy: RecoveryStrategy::Fail }
}
#[must_use]
pub fn with_strategy(mut self, error_type: &str, strategy: RecoveryStrategy) -> Self {
self.strategies.insert(error_type.to_string(), strategy);
self
}
#[must_use]
pub fn set_default(mut self, strategy: RecoveryStrategy) -> Self {
self.default_strategy = strategy;
self
}
pub fn get_strategy(&self, error_type: &str) -> &RecoveryStrategy {
self.strategies.get(error_type).unwrap_or(&self.default_strategy)
}
pub fn handle<T, E>(&self, result: CoreResult<T, E>, context: &str) -> RecoveryResult<T>
where
E: AsRef<str> + std::fmt::Debug + Clone + Into<Error>,
{
match result {
Ok(value) => RecoveryResult::Success(value),
Err(error) => {
let error_type = error.as_ref();
let strategy = self.get_strategy(error_type);
match strategy {
RecoveryStrategy::Fail => RecoveryResult::Failed(error.into()),
RecoveryStrategy::LogAndContinue(level) => {
match level {
LogLevel::Debug => log::debug!(
target: "recovery",
"Recovery executed - Context: {context} | Error: {error:?} | Strategy: LogAndContinue(Debug)"
),
LogLevel::Info => log::info!(
target: "recovery",
"Recovery executed - Context: {context} | Error: {error:?} | Strategy: LogAndContinue(Info)"
),
LogLevel::Warn => log::warn!(
target: "recovery",
"Recovery warning - Context: {context} | Error: {error:?} | Strategy: LogAndContinue(Warn)"
),
LogLevel::Error => log::error!(
target: "recovery",
"Recovery error - Context: {context} | Error: {error:?} | Strategy: LogAndContinue(Error)"
),
}
RecoveryResult::Recovered {
value: None,
reason: format!("Logged and continued after error: {error:?}"),
}
}
RecoveryStrategy::UseDefault(reason) => {
RecoveryResult::Recovered { value: None, reason: reason.clone() }
}
RecoveryStrategy::Retry { max_attempts: _, backoff_ms } => {
RecoveryResult::Retrying {
attempt: 1,
next_backoff: Duration::from_millis(*backoff_ms),
}
}
RecoveryStrategy::Graceful(message) => {
RecoveryResult::Recovered { value: None, reason: message.clone() }
}
}
}
}
}
#[allow(dead_code)]
fn analyze_error_context<E: std::error::Error>(level: LogLevel, context: &str, error: &E) {
let error_chain = Self::build_error_chain(error);
let error_type = std::any::type_name::<E>();
log::info!(
target: "recovery.analysis",
"Error Analysis - Level: {:?} | Context: {} | Type: {} | Chain Length: {} | Root Cause: {}",
level,
context,
error_type,
error_chain.len(),
error_chain.first().unwrap_or(&"Unknown".to_string())
);
for (index, error_msg) in error_chain.iter().enumerate() {
log::debug!(
target: "recovery.chain",
"Error Chain[{index}]: {error_msg}"
);
}
}
fn build_error_chain<E: std::error::Error>(error: &E) -> Vec<String> {
let mut chain = Vec::new();
let mut current_error: &dyn std::error::Error = error;
loop {
chain.push(current_error.to_string());
match current_error.source() {
Some(source) => current_error = source,
None => break,
}
}
chain
}
}