use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::fmt;
use std::sync::Arc;
use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
use thiserror::Error;
use tokio::sync::RwLock;
#[derive(Debug, Clone, Serialize, Deserialize, Error)]
#[error("{message}")]
pub struct AdvancedError {
pub category: ErrorCategory,
pub severity: ErrorSeverity,
pub message: String,
pub technical_details: String,
pub context: ErrorContext,
pub recovery_suggestions: Vec<RecoverySuggestion>,
pub related_errors: Vec<String>,
pub timestamp: u64,
pub error_id: String,
pub recoverable: bool,
pub retry_info: Option<RetryInfo>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ErrorCategory {
Configuration,
Network,
FileSystem,
Memory,
ModelLoading,
AudioProcessing,
Synthesis,
Authentication,
Permission,
ResourceExhaustion,
Dependency,
Hardware,
UserInput,
Internal,
ExternalService,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum ErrorSeverity {
Info,
Warning,
Error,
Critical,
Fatal,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorContext {
pub operation: String,
pub user: Option<String>,
pub session_id: Option<String>,
pub request_id: Option<String>,
pub component: String,
pub function: Option<String>,
pub location: Option<String>,
pub parameters: HashMap<String, String>,
pub system_state: SystemState,
pub performance_metrics: Option<ErrorTimeMetrics>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SystemState {
pub available_memory_bytes: u64,
pub cpu_usage_percent: f64,
pub active_operations: usize,
pub queue_depth: usize,
pub last_success_time: Option<u64>,
pub uptime_seconds: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorTimeMetrics {
pub latency_ms: u64,
pub throughput: f64,
pub memory_usage_mb: f64,
pub recent_error_rate: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecoverySuggestion {
pub category: RecoveryCategory,
pub priority: u8,
pub suggestion: String,
pub steps: Vec<String>,
pub estimated_time: Duration,
pub difficulty: DifficultyLevel,
pub success_probability: f64,
pub requires_user_action: bool,
pub automated_actions: Vec<AutomatedAction>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum RecoveryCategory {
AutomaticRecovery,
ConfigurationFix,
ResourceOptimization,
RetryOptimization,
SystemRestart,
SoftwareUpdate,
HardwareCheck,
NetworkTroubleshooting,
PermissionFix,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum DifficultyLevel {
Trivial,
Easy,
Medium,
Hard,
Expert,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AutomatedAction {
pub action_type: ActionType,
pub description: String,
pub parameters: HashMap<String, String>,
pub safe_to_automate: bool,
pub execution_time: Duration,
pub dependencies: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ActionType {
RestartService,
ClearCache,
AdjustConfiguration,
RetryOperation,
ReduceResources,
EnableFallback,
UpdateSoftware,
CheckResources,
ValidateConfiguration,
RepairData,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RetryInfo {
pub attempt: usize,
pub max_attempts: usize,
pub retry_delay: Duration,
pub backoff_strategy: BackoffStrategy,
pub last_retry: Option<u64>,
pub success_history: Vec<bool>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum BackoffStrategy {
Fixed,
Linear,
Exponential,
Fibonacci,
Custom(Vec<u64>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorPattern {
pub pattern_id: String,
pub description: String,
pub categories: Vec<ErrorCategory>,
pub min_occurrences: usize,
pub time_window: Duration,
pub recovery_strategy: RecoveryStrategy,
pub confidence: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecoveryStrategy {
pub name: String,
pub automatic_actions: Vec<AutomatedAction>,
pub manual_steps: Vec<String>,
pub conditions: Vec<String>,
pub success_rate: f64,
pub recovery_time: Duration,
}
pub struct AdvancedErrorHandler {
error_history: Arc<RwLock<Vec<AdvancedError>>>,
detected_patterns: Arc<RwLock<HashMap<String, ErrorPattern>>>,
recovery_stats: Arc<RwLock<RecoveryStatistics>>,
config: ErrorHandlerConfig,
pattern_rules: Vec<ErrorPattern>,
action_handlers: HashMap<ActionType, Box<dyn ActionHandler>>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct RecoveryStatistics {
pub total_errors: u64,
pub recovery_attempts: u64,
pub successful_recoveries: u64,
pub success_rate: f64,
pub avg_recovery_time_ms: f64,
pub category_stats: HashMap<ErrorCategory, CategoryStats>,
pub pattern_stats: HashMap<String, PatternStats>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct CategoryStats {
pub count: u64,
pub recovery_rate: f64,
pub avg_recovery_time_ms: f64,
pub best_recovery_method: Option<RecoveryCategory>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct PatternStats {
pub detection_count: u64,
pub recovery_attempts: u64,
pub successful_recoveries: u64,
pub success_rate: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorHandlerConfig {
pub enable_pattern_detection: bool,
pub enable_auto_recovery: bool,
pub max_error_history: usize,
pub pattern_sensitivity: f64,
pub max_auto_recovery_attempts: usize,
pub reporting_config: ErrorReportingConfig,
pub recovery_timeout: Duration,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorReportingConfig {
pub detailed_logging: bool,
pub log_file_path: Option<String>,
pub enable_telemetry: bool,
pub telemetry_endpoint: Option<String>,
pub enable_notifications: bool,
pub notification_threshold: ErrorSeverity,
}
pub trait ActionHandler: Send + Sync {
fn execute(&self, action: &AutomatedAction) -> Result<ActionResult, ActionError>;
fn can_execute(&self, action: &AutomatedAction) -> bool;
fn estimated_time(&self, action: &AutomatedAction) -> Duration;
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ActionResult {
pub success: bool,
pub message: String,
pub execution_time: Duration,
pub data: HashMap<String, String>,
}
#[derive(Debug, Error)]
pub enum ActionError {
#[error("Action execution failed: {message}")]
ExecutionFailed { message: String },
#[error("Action not supported: {action_type:?}")]
NotSupported { action_type: ActionType },
#[error("Action timed out after {timeout:?}")]
Timeout { timeout: Duration },
#[error("Action dependencies not met: {missing:?}")]
DependenciesMissing { missing: Vec<String> },
}
impl AdvancedErrorHandler {
pub fn new(config: ErrorHandlerConfig) -> Self {
Self {
error_history: Arc::new(RwLock::new(Vec::new())),
detected_patterns: Arc::new(RwLock::new(HashMap::new())),
recovery_stats: Arc::new(RwLock::new(RecoveryStatistics::default())),
config,
pattern_rules: Self::default_pattern_rules(),
action_handlers: Self::default_action_handlers(),
}
}
pub async fn handle_error(&self, error: AdvancedError) -> ErrorHandlingResult {
tracing::error!(
"Handling advanced error: {} ({})",
error.message,
error.error_id
);
self.record_error(&error).await;
self.update_statistics(&error).await;
let detected_patterns = if self.config.enable_pattern_detection {
self.detect_patterns(&error).await
} else {
Vec::new()
};
let recovery_result = if self.config.enable_auto_recovery && error.recoverable {
self.attempt_recovery(&error).await
} else {
RecoveryResult::NotAttempted
};
let user_report = self
.generate_user_report(&error, &detected_patterns, &recovery_result)
.await;
ErrorHandlingResult {
error_id: error.error_id.clone(),
handled: true,
recovery_result,
detected_patterns,
user_report,
automated_actions_taken: self.get_automated_actions(&error).await,
recommendations: error.recovery_suggestions.clone(),
}
}
async fn record_error(&self, error: &AdvancedError) {
let mut history = self.error_history.write().await;
history.push(error.clone());
if history.len() > self.config.max_error_history {
history.remove(0);
}
}
async fn update_statistics(&self, error: &AdvancedError) {
let mut stats = self.recovery_stats.write().await;
stats.total_errors += 1;
let category_stats = stats.category_stats.entry(error.category).or_default();
category_stats.count += 1;
}
async fn detect_patterns(&self, current_error: &AdvancedError) -> Vec<String> {
let mut detected = Vec::new();
let history = self.error_history.read().await;
for pattern in &self.pattern_rules {
if self.matches_pattern(pattern, current_error, &history) {
detected.push(pattern.pattern_id.clone());
let mut patterns = self.detected_patterns.write().await;
patterns.insert(pattern.pattern_id.clone(), pattern.clone());
}
}
detected
}
fn matches_pattern(
&self,
pattern: &ErrorPattern,
error: &AdvancedError,
history: &[AdvancedError],
) -> bool {
if !pattern.categories.contains(&error.category) {
return false;
}
let cutoff_time = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
.saturating_sub(pattern.time_window.as_secs());
let recent_similar_errors = history
.iter()
.filter(|e| e.timestamp >= cutoff_time)
.filter(|e| pattern.categories.contains(&e.category))
.count();
recent_similar_errors >= pattern.min_occurrences
}
async fn attempt_recovery(&self, error: &AdvancedError) -> RecoveryResult {
tracing::info!(
"Attempting automatic recovery for error: {}",
error.error_id
);
let mut stats = self.recovery_stats.write().await;
stats.recovery_attempts += 1;
drop(stats);
let mut sorted_suggestions = error.recovery_suggestions.clone();
sorted_suggestions.sort_by_key(|b| std::cmp::Reverse(b.priority));
for suggestion in sorted_suggestions {
if suggestion.category == RecoveryCategory::AutomaticRecovery {
match self
.execute_recovery_actions(&suggestion.automated_actions)
.await
{
Ok(results) => {
if results.iter().all(|r| r.success) {
let mut stats = self.recovery_stats.write().await;
stats.successful_recoveries += 1;
stats.success_rate =
stats.successful_recoveries as f64 / stats.recovery_attempts as f64;
return RecoveryResult::Successful {
method: suggestion.category,
actions_taken: results,
recovery_time: suggestion.estimated_time,
};
}
}
Err(e) => {
tracing::warn!("Recovery action failed: {}", e);
}
}
}
}
RecoveryResult::Failed {
reason: "No successful automatic recovery method found".to_string(),
attempted_methods: error
.recovery_suggestions
.iter()
.map(|s| s.category.clone())
.collect(),
}
}
async fn execute_recovery_actions(
&self,
actions: &[AutomatedAction],
) -> Result<Vec<ActionResult>, ActionError> {
let mut results = Vec::new();
for action in actions {
if let Some(handler) = self.action_handlers.get(&action.action_type) {
if handler.can_execute(action) {
match handler.execute(action) {
Ok(result) => results.push(result),
Err(e) => return Err(e),
}
} else {
return Err(ActionError::NotSupported {
action_type: action.action_type.clone(),
});
}
}
}
Ok(results)
}
async fn generate_user_report(
&self,
error: &AdvancedError,
patterns: &[String],
recovery: &RecoveryResult,
) -> UserErrorReport {
UserErrorReport {
title: self.generate_user_friendly_title(error),
summary: self.generate_error_summary(error),
impact: self.assess_user_impact(error),
what_happened: self.explain_what_happened(error),
why_it_happened: self.explain_why_it_happened(error, patterns),
what_we_did: self.explain_recovery_actions(recovery),
what_you_can_do: self.generate_user_actions(error),
prevention_tips: self.generate_prevention_tips(error),
technical_details: if self.config.reporting_config.detailed_logging {
Some(error.technical_details.clone())
} else {
None
},
support_info: self.generate_support_info(error),
}
}
fn generate_user_friendly_title(&self, error: &AdvancedError) -> String {
match error.category {
ErrorCategory::Network => "Network Connection Issue".to_string(),
ErrorCategory::Memory => "Memory Issue Detected".to_string(),
ErrorCategory::ModelLoading => "Model Loading Problem".to_string(),
ErrorCategory::AudioProcessing => "Audio Processing Error".to_string(),
ErrorCategory::Synthesis => "Voice Synthesis Issue".to_string(),
ErrorCategory::Configuration => "Configuration Problem".to_string(),
ErrorCategory::FileSystem => "File System Error".to_string(),
ErrorCategory::Authentication => "Authentication Issue".to_string(),
ErrorCategory::Permission => "Permission Error".to_string(),
ErrorCategory::ResourceExhaustion => "System Resources Low".to_string(),
ErrorCategory::Dependency => "Dependency Issue".to_string(),
ErrorCategory::Hardware => "Hardware Problem Detected".to_string(),
ErrorCategory::UserInput => "Input Validation Error".to_string(),
ErrorCategory::Internal => "Internal System Error".to_string(),
ErrorCategory::ExternalService => "External Service Issue".to_string(),
}
}
fn generate_error_summary(&self, error: &AdvancedError) -> String {
match error.severity {
ErrorSeverity::Info => format!("Information: {}", error.message),
ErrorSeverity::Warning => {
format!("Warning: {} This may affect performance.", error.message)
}
ErrorSeverity::Error => format!(
"Error: {} The current operation could not be completed.",
error.message
),
ErrorSeverity::Critical => format!(
"Critical Issue: {} System functionality may be impaired.",
error.message
),
ErrorSeverity::Fatal => format!(
"Fatal Error: {} System cannot continue normal operation.",
error.message
),
}
}
fn assess_user_impact(&self, error: &AdvancedError) -> UserImpact {
match (error.severity, error.category) {
(ErrorSeverity::Fatal, _) => UserImpact::Severe,
(ErrorSeverity::Critical, _) => UserImpact::High,
(ErrorSeverity::Error, ErrorCategory::Synthesis) => UserImpact::Medium,
(ErrorSeverity::Error, ErrorCategory::AudioProcessing) => UserImpact::Medium,
(ErrorSeverity::Warning, _) => UserImpact::Low,
(ErrorSeverity::Info, _) => UserImpact::None,
_ => UserImpact::Medium,
}
}
fn explain_what_happened(&self, error: &AdvancedError) -> String {
format!(
"While performing '{}', an issue occurred in the {} component. {}",
error.context.operation,
error.context.component,
match error.category {
ErrorCategory::Network =>
"The system could not connect to the required network service.",
ErrorCategory::Memory => "The system ran low on available memory.",
ErrorCategory::ModelLoading => "The voice model could not be loaded properly.",
ErrorCategory::AudioProcessing =>
"The audio data could not be processed as expected.",
ErrorCategory::Synthesis => "The voice synthesis process encountered an issue.",
_ => "An unexpected condition was encountered.",
}
)
}
fn explain_why_it_happened(&self, error: &AdvancedError, patterns: &[String]) -> String {
if !patterns.is_empty() {
format!(
"This appears to be part of a known pattern: {}. Common causes include resource constraints, network instability, or configuration issues.",
patterns.join(", ")
)
} else {
match error.category {
ErrorCategory::Network => "This could be due to internet connectivity issues, firewall restrictions, or service outages.".to_string(),
ErrorCategory::Memory => "This typically happens when the system is processing large amounts of data or when other applications are using significant memory.".to_string(),
ErrorCategory::ModelLoading => "This may occur if model files are corrupted, missing, or incompatible with the current system.".to_string(),
_ => "The exact cause is being investigated. This may be a temporary issue.".to_string(),
}
}
}
fn explain_recovery_actions(&self, recovery: &RecoveryResult) -> String {
match recovery {
RecoveryResult::Successful {
method,
actions_taken,
..
} => {
format!(
"We automatically attempted to resolve this issue using {} and took {} recovery actions. The issue appears to be resolved.",
Self::recovery_method_description(method),
actions_taken.len()
)
}
RecoveryResult::Failed {
attempted_methods, ..
} => {
format!(
"We attempted automatic recovery using {} methods, but the issue persists and requires manual intervention.",
attempted_methods.len()
)
}
RecoveryResult::NotAttempted => {
"No automatic recovery was attempted for this type of issue.".to_string()
}
}
}
fn generate_user_actions(&self, error: &AdvancedError) -> Vec<String> {
let mut actions = Vec::new();
match error.category {
ErrorCategory::Network => {
actions.push("Check your internet connection".to_string());
actions.push("Try again in a few moments".to_string());
actions.push("Check if any firewalls are blocking the connection".to_string());
}
ErrorCategory::Memory => {
actions.push("Close other applications to free up memory".to_string());
actions.push("Try processing smaller amounts of data at once".to_string());
actions.push("Restart the application if the issue persists".to_string());
}
ErrorCategory::ModelLoading => {
actions.push("Verify that all required model files are present".to_string());
actions.push("Try re-downloading the models if available".to_string());
actions.push("Check available disk space".to_string());
}
_ => {
actions.push("Try the operation again".to_string());
actions.push("Check the system logs for more details".to_string());
}
}
for suggestion in &error.recovery_suggestions {
if suggestion.requires_user_action && suggestion.difficulty == DifficultyLevel::Easy {
actions.push(suggestion.suggestion.clone());
}
}
actions
}
fn generate_prevention_tips(&self, error: &AdvancedError) -> Vec<String> {
match error.category {
ErrorCategory::Network => vec![
"Ensure stable internet connection before starting operations".to_string(),
"Consider using offline mode when available".to_string(),
],
ErrorCategory::Memory => vec![
"Close unnecessary applications before processing large tasks".to_string(),
"Process data in smaller batches".to_string(),
"Consider upgrading system memory if this occurs frequently".to_string(),
],
ErrorCategory::ModelLoading => vec![
"Regularly verify model file integrity".to_string(),
"Keep models updated to the latest versions".to_string(),
"Ensure sufficient disk space for model storage".to_string(),
],
_ => vec![
"Keep the application updated to the latest version".to_string(),
"Monitor system resources during heavy operations".to_string(),
],
}
}
fn generate_support_info(&self, error: &AdvancedError) -> SupportInfo {
SupportInfo {
error_id: error.error_id.clone(),
timestamp: error.timestamp,
component: error.context.component.clone(),
severity: error.severity,
category: error.category,
session_id: error.context.session_id.clone(),
request_id: error.context.request_id.clone(),
support_url: Some("https://support.voirs.com".to_string()),
documentation_links: self.get_relevant_documentation_links(&error.category),
}
}
async fn get_automated_actions(&self, error: &AdvancedError) -> Vec<String> {
error
.recovery_suggestions
.iter()
.filter(|s| s.category == RecoveryCategory::AutomaticRecovery)
.flat_map(|s| s.automated_actions.iter())
.map(|a| a.description.clone())
.collect()
}
fn get_relevant_documentation_links(&self, category: &ErrorCategory) -> Vec<String> {
match category {
ErrorCategory::Network => vec![
"https://docs.voirs.com/troubleshooting/network".to_string(),
"https://docs.voirs.com/configuration/connectivity".to_string(),
],
ErrorCategory::Memory => vec![
"https://docs.voirs.com/performance/memory-optimization".to_string(),
"https://docs.voirs.com/troubleshooting/performance".to_string(),
],
ErrorCategory::ModelLoading => vec![
"https://docs.voirs.com/models/installation".to_string(),
"https://docs.voirs.com/troubleshooting/models".to_string(),
],
_ => vec!["https://docs.voirs.com/troubleshooting".to_string()],
}
}
fn recovery_method_description(method: &RecoveryCategory) -> &'static str {
match method {
RecoveryCategory::AutomaticRecovery => "automatic system recovery",
RecoveryCategory::ConfigurationFix => "configuration adjustment",
RecoveryCategory::ResourceOptimization => "resource optimization",
RecoveryCategory::RetryOptimization => "intelligent retry",
RecoveryCategory::SystemRestart => "system restart",
RecoveryCategory::SoftwareUpdate => "software update",
RecoveryCategory::HardwareCheck => "hardware validation",
RecoveryCategory::NetworkTroubleshooting => "network diagnostics",
RecoveryCategory::PermissionFix => "permission correction",
}
}
fn default_pattern_rules() -> Vec<ErrorPattern> {
vec![
ErrorPattern {
pattern_id: "network_instability".to_string(),
description: "Repeated network connection failures".to_string(),
categories: vec![ErrorCategory::Network, ErrorCategory::ExternalService],
min_occurrences: 3,
time_window: Duration::from_secs(300), recovery_strategy: RecoveryStrategy {
name: "Network Recovery".to_string(),
automatic_actions: vec![AutomatedAction {
action_type: ActionType::RetryOperation,
description: "Retry with exponential backoff".to_string(),
parameters: HashMap::new(),
safe_to_automate: true,
execution_time: Duration::from_secs(30),
dependencies: vec![],
}],
manual_steps: vec!["Check network connection".to_string()],
conditions: vec!["Network errors > 3 in 5 minutes".to_string()],
success_rate: 0.8,
recovery_time: Duration::from_secs(60),
},
confidence: 0.9,
},
ErrorPattern {
pattern_id: "memory_pressure".to_string(),
description: "System running low on memory".to_string(),
categories: vec![ErrorCategory::Memory, ErrorCategory::ResourceExhaustion],
min_occurrences: 2,
time_window: Duration::from_secs(120), recovery_strategy: RecoveryStrategy {
name: "Memory Optimization".to_string(),
automatic_actions: vec![
AutomatedAction {
action_type: ActionType::ClearCache,
description: "Clear system caches".to_string(),
parameters: HashMap::new(),
safe_to_automate: true,
execution_time: Duration::from_secs(10),
dependencies: vec![],
},
AutomatedAction {
action_type: ActionType::ReduceResources,
description: "Reduce memory usage".to_string(),
parameters: HashMap::new(),
safe_to_automate: true,
execution_time: Duration::from_secs(5),
dependencies: vec![],
},
],
manual_steps: vec!["Close unnecessary applications".to_string()],
conditions: vec!["Memory errors in short timespan".to_string()],
success_rate: 0.7,
recovery_time: Duration::from_secs(30),
},
confidence: 0.85,
},
]
}
fn default_action_handlers() -> HashMap<ActionType, Box<dyn ActionHandler>> {
HashMap::new()
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ErrorHandlingResult {
pub error_id: String,
pub handled: bool,
pub recovery_result: RecoveryResult,
pub detected_patterns: Vec<String>,
pub user_report: UserErrorReport,
pub automated_actions_taken: Vec<String>,
pub recommendations: Vec<RecoverySuggestion>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RecoveryResult {
Successful {
method: RecoveryCategory,
actions_taken: Vec<ActionResult>,
recovery_time: Duration,
},
Failed {
reason: String,
attempted_methods: Vec<RecoveryCategory>,
},
NotAttempted,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct UserErrorReport {
pub title: String,
pub summary: String,
pub impact: UserImpact,
pub what_happened: String,
pub why_it_happened: String,
pub what_we_did: String,
pub what_you_can_do: Vec<String>,
pub prevention_tips: Vec<String>,
pub technical_details: Option<String>,
pub support_info: SupportInfo,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum UserImpact {
None,
Low,
Medium,
High,
Severe,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SupportInfo {
pub error_id: String,
pub timestamp: u64,
pub component: String,
pub severity: ErrorSeverity,
pub category: ErrorCategory,
pub session_id: Option<String>,
pub request_id: Option<String>,
pub support_url: Option<String>,
pub documentation_links: Vec<String>,
}
impl Default for ErrorHandlerConfig {
fn default() -> Self {
Self {
enable_pattern_detection: true,
enable_auto_recovery: true,
max_error_history: 1000,
pattern_sensitivity: 0.8,
max_auto_recovery_attempts: 3,
reporting_config: ErrorReportingConfig::default(),
recovery_timeout: Duration::from_secs(60),
}
}
}
impl Default for ErrorReportingConfig {
fn default() -> Self {
Self {
detailed_logging: true,
log_file_path: Some("voirs-errors.log".to_string()),
enable_telemetry: false,
telemetry_endpoint: None,
enable_notifications: true,
notification_threshold: ErrorSeverity::Error,
}
}
}
impl fmt::Display for ErrorSeverity {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorSeverity::Info => write!(f, "INFO"),
ErrorSeverity::Warning => write!(f, "WARNING"),
ErrorSeverity::Error => write!(f, "ERROR"),
ErrorSeverity::Critical => write!(f, "CRITICAL"),
ErrorSeverity::Fatal => write!(f, "FATAL"),
}
}
}
impl fmt::Display for ErrorCategory {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ErrorCategory::Configuration => write!(f, "Configuration"),
ErrorCategory::Network => write!(f, "Network"),
ErrorCategory::FileSystem => write!(f, "FileSystem"),
ErrorCategory::Memory => write!(f, "Memory"),
ErrorCategory::ModelLoading => write!(f, "ModelLoading"),
ErrorCategory::AudioProcessing => write!(f, "AudioProcessing"),
ErrorCategory::Synthesis => write!(f, "Synthesis"),
ErrorCategory::Authentication => write!(f, "Authentication"),
ErrorCategory::Permission => write!(f, "Permission"),
ErrorCategory::ResourceExhaustion => write!(f, "ResourceExhaustion"),
ErrorCategory::Dependency => write!(f, "Dependency"),
ErrorCategory::Hardware => write!(f, "Hardware"),
ErrorCategory::UserInput => write!(f, "UserInput"),
ErrorCategory::Internal => write!(f, "Internal"),
ErrorCategory::ExternalService => write!(f, "ExternalService"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_error_handler_creation() {
let config = ErrorHandlerConfig::default();
let handler = AdvancedErrorHandler::new(config);
assert!(handler.pattern_rules.len() > 0);
}
#[tokio::test]
async fn test_error_handling() {
let config = ErrorHandlerConfig::default();
let handler = AdvancedErrorHandler::new(config);
let error = AdvancedError {
category: ErrorCategory::Network,
severity: ErrorSeverity::Error,
message: "Connection failed".to_string(),
technical_details: "TCP connection timeout".to_string(),
context: ErrorContext {
operation: "test_operation".to_string(),
user: None,
session_id: None,
request_id: None,
component: "network_client".to_string(),
function: Some("connect".to_string()),
location: None,
parameters: HashMap::new(),
system_state: SystemState {
available_memory_bytes: 1000000,
cpu_usage_percent: 50.0,
active_operations: 1,
queue_depth: 0,
last_success_time: None,
uptime_seconds: 3600,
},
performance_metrics: None,
},
recovery_suggestions: vec![],
related_errors: vec![],
timestamp: SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap()
.as_secs(),
error_id: "test_error_123".to_string(),
recoverable: true,
retry_info: Some(RetryInfo {
attempt: 0,
max_attempts: 3,
retry_delay: Duration::from_secs(5),
backoff_strategy: BackoffStrategy::Exponential,
last_retry: None,
success_history: vec![],
}),
};
let result = handler.handle_error(error).await;
assert!(result.handled);
assert_eq!(result.error_id, "test_error_123");
}
#[test]
fn test_error_severity_ordering() {
assert!(ErrorSeverity::Fatal > ErrorSeverity::Critical);
assert!(ErrorSeverity::Critical > ErrorSeverity::Error);
assert!(ErrorSeverity::Error > ErrorSeverity::Warning);
assert!(ErrorSeverity::Warning > ErrorSeverity::Info);
}
#[test]
fn test_backoff_strategy() {
let retry_info = RetryInfo {
attempt: 2,
max_attempts: 5,
retry_delay: Duration::from_secs(10),
backoff_strategy: BackoffStrategy::Exponential,
last_retry: None,
success_history: vec![false, false],
};
assert_eq!(retry_info.backoff_strategy, BackoffStrategy::Exponential);
assert_eq!(retry_info.attempt, 2);
}
#[test]
fn test_user_impact_assessment() {
let config = ErrorHandlerConfig::default();
let handler = AdvancedErrorHandler::new(config);
let fatal_error = AdvancedError {
severity: ErrorSeverity::Fatal,
category: ErrorCategory::Internal,
message: "Fatal error".to_string(),
technical_details: "System crash".to_string(),
context: ErrorContext {
operation: "test".to_string(),
user: None,
session_id: None,
request_id: None,
component: "test".to_string(),
function: None,
location: None,
parameters: HashMap::new(),
system_state: SystemState {
available_memory_bytes: 0,
cpu_usage_percent: 0.0,
active_operations: 0,
queue_depth: 0,
last_success_time: None,
uptime_seconds: 0,
},
performance_metrics: None,
},
recovery_suggestions: vec![],
related_errors: vec![],
timestamp: 0,
error_id: "test".to_string(),
recoverable: false,
retry_info: None,
};
let impact = handler.assess_user_impact(&fatal_error);
assert_eq!(impact, UserImpact::Severe);
}
}