use crate::core::error::{ErrorKind, ErrorSeverity, MemScopeError};
use std::collections::HashMap;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::{Arc, Mutex};
type ErrorHandlerFn = Box<dyn Fn(&MemScopeError) + Send + Sync>;
pub struct ErrorHandler {
error_counts: HashMap<ErrorKind, AtomicUsize>,
recent_errors: Arc<Mutex<Vec<MemScopeError>>>,
max_recent_errors: usize,
reporter: ErrorReporter,
}
pub struct ErrorReporter {
log_to_stderr: bool,
store_errors: bool,
min_severity: ErrorSeverity,
custom_handlers: HashMap<ErrorSeverity, ErrorHandlerFn>,
}
impl ErrorHandler {
pub fn new() -> Self {
Self {
error_counts: HashMap::new(),
recent_errors: Arc::new(Mutex::new(Vec::new())),
max_recent_errors: 100,
reporter: ErrorReporter::new(),
}
}
pub fn with_config(max_recent_errors: usize, reporter: ErrorReporter) -> Self {
Self {
error_counts: HashMap::new(),
recent_errors: Arc::new(Mutex::new(Vec::new())),
max_recent_errors,
reporter,
}
}
pub fn handle_error(&mut self, error: MemScopeError) -> ErrorResponse {
self.update_statistics(&error);
if self.reporter.store_errors {
self.store_recent_error(error.clone());
}
let reported = self.reporter.report_error(&error);
let response = self.determine_response(&error);
if reported && response.should_log() {
self.log_response(&error, &response);
}
response
}
pub fn get_error_counts(&self) -> HashMap<ErrorKind, usize> {
self.error_counts
.iter()
.map(|(kind, count)| (*kind, count.load(Ordering::Relaxed)))
.collect()
}
pub fn get_recent_errors(&self) -> Vec<MemScopeError> {
self.recent_errors
.lock()
.map(|errors| errors.clone())
.unwrap_or_default()
}
pub fn clear_statistics(&mut self) {
for count in self.error_counts.values() {
count.store(0, Ordering::Relaxed);
}
if let Ok(mut recent) = self.recent_errors.lock() {
recent.clear();
}
}
pub fn get_error_frequency(&self) -> ErrorFrequencyAnalysis {
let counts = self.get_error_counts();
let total_errors: usize = counts.values().sum();
let most_common = counts
.iter()
.max_by_key(|(_, count)| *count)
.map(|(kind, count)| (*kind, *count));
let error_rate = if let Ok(recent) = self.recent_errors.lock() {
if recent.is_empty() {
0.0
} else {
let oldest_time = recent.first().map(|e| e.context().timestamp);
let newest_time = recent.last().map(|e| e.context().timestamp);
if let (Some(oldest), Some(newest)) = (oldest_time, newest_time) {
match newest.duration_since(oldest) {
Ok(duration) => {
let duration_secs = duration.as_secs_f64();
if duration_secs > 0.0 {
recent.len() as f64 / duration_secs
} else {
0.0
}
}
Err(_) => 0.0,
}
} else {
0.0
}
}
} else {
0.0
};
ErrorFrequencyAnalysis {
total_errors,
most_common_error: most_common,
errors_per_second: error_rate,
error_distribution: counts,
}
}
fn update_statistics(&mut self, error: &MemScopeError) {
let counter = self
.error_counts
.entry(error.kind())
.or_insert_with(|| AtomicUsize::new(0));
counter.fetch_add(1, Ordering::Relaxed);
}
fn store_recent_error(&self, error: MemScopeError) {
if let Ok(mut recent) = self.recent_errors.lock() {
recent.push(error);
if recent.len() > self.max_recent_errors {
let excess = recent.len() - self.max_recent_errors;
recent.drain(0..excess);
}
}
}
fn determine_response(&self, error: &MemScopeError) -> ErrorResponse {
match error.severity() {
ErrorSeverity::Warning => ErrorResponse::Continue,
ErrorSeverity::Error => {
if self.is_frequent_error(&error.kind()) {
ErrorResponse::Throttle
} else {
ErrorResponse::Retry
}
}
ErrorSeverity::Critical => ErrorResponse::Fallback,
ErrorSeverity::Fatal => ErrorResponse::Abort,
}
}
fn is_frequent_error(&self, kind: &ErrorKind) -> bool {
self.error_counts
.get(kind)
.map(|count| count.load(Ordering::Relaxed) > 10)
.unwrap_or(false)
}
fn log_response(&self, error: &MemScopeError, response: &ErrorResponse) {
if self.reporter.log_to_stderr {
eprintln!(
"ErrorHandler: {} -> Response: {:?}",
error.description(),
response
);
}
}
}
impl ErrorReporter {
pub fn new() -> Self {
Self {
log_to_stderr: true,
store_errors: true,
min_severity: ErrorSeverity::Warning,
custom_handlers: HashMap::new(),
}
}
pub fn with_logging(mut self, enabled: bool) -> Self {
self.log_to_stderr = enabled;
self
}
pub fn with_storage(mut self, enabled: bool) -> Self {
self.store_errors = enabled;
self
}
pub fn with_min_severity(mut self, severity: ErrorSeverity) -> Self {
self.min_severity = severity;
self
}
pub fn with_custom_handler<F>(mut self, severity: ErrorSeverity, handler: F) -> Self
where
F: Fn(&MemScopeError) + Send + Sync + 'static,
{
self.custom_handlers.insert(severity, Box::new(handler));
self
}
pub fn report_error(&self, error: &MemScopeError) -> bool {
if error.severity() < self.min_severity {
return false;
}
if let Some(handler) = self.custom_handlers.get(&error.severity()) {
handler(error);
}
if self.log_to_stderr {
eprintln!("MemScope Error: {}", error);
}
true
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum ErrorResponse {
Continue,
Retry,
Throttle,
Fallback,
Abort,
}
impl ErrorResponse {
pub fn should_log(&self) -> bool {
!matches!(self, ErrorResponse::Continue)
}
pub fn should_retry(&self) -> bool {
matches!(self, ErrorResponse::Retry)
}
pub fn should_abort(&self) -> bool {
matches!(self, ErrorResponse::Abort)
}
}
#[derive(Debug, Clone)]
pub struct ErrorFrequencyAnalysis {
pub total_errors: usize,
pub most_common_error: Option<(ErrorKind, usize)>,
pub errors_per_second: f64,
pub error_distribution: HashMap<ErrorKind, usize>,
}
impl ErrorFrequencyAnalysis {
pub fn is_error_rate_high(&self) -> bool {
self.errors_per_second > 1.0 || self.total_errors > 100
}
pub fn get_primary_concern(&self) -> Option<ErrorKind> {
self.most_common_error.as_ref().map(|(kind, _)| *kind)
}
pub fn summary(&self) -> String {
format!(
"Error Analysis: {} total errors, {:.2} errors/sec, primary concern: {:?}",
self.total_errors,
self.errors_per_second,
self.get_primary_concern()
)
}
}
impl Default for ErrorHandler {
fn default() -> Self {
Self::new()
}
}
impl Default for ErrorReporter {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_handler_basic() {
let mut handler = ErrorHandler::new();
let error = MemScopeError::new(ErrorKind::MemoryError, "test error");
let response = handler.handle_error(error);
assert_eq!(response, ErrorResponse::Retry);
let counts = handler.get_error_counts();
assert_eq!(counts.get(&ErrorKind::MemoryError), Some(&1));
}
#[test]
fn test_error_frequency_analysis() {
let mut handler = ErrorHandler::new();
for _ in 0..5 {
let error = MemScopeError::new(ErrorKind::CacheError, "cache miss");
handler.handle_error(error);
}
for _ in 0..3 {
let error = MemScopeError::new(ErrorKind::MemoryError, "allocation failed");
handler.handle_error(error);
}
let analysis = handler.get_error_frequency();
assert_eq!(analysis.total_errors, 8);
assert_eq!(analysis.get_primary_concern(), Some(ErrorKind::CacheError));
}
#[test]
fn test_error_reporter_configuration() {
let reporter = ErrorReporter::new()
.with_logging(false)
.with_storage(true)
.with_min_severity(ErrorSeverity::Error);
let validation_err = MemScopeError::with_context(
ErrorKind::ValidationError,
ErrorSeverity::Warning,
"test_warning",
"test",
);
assert!(reporter.report_error(&validation_err));
let error = MemScopeError::with_context(
ErrorKind::MemoryError,
ErrorSeverity::Error,
"test_error",
"test",
);
assert!(reporter.report_error(&error));
}
#[test]
fn test_error_response_strategies() {
let handler = ErrorHandler::new();
let validation_err = MemScopeError::with_context(
ErrorKind::ValidationError,
ErrorSeverity::Warning,
"test",
"test",
);
let response = handler.determine_response(&validation_err);
assert_eq!(response, ErrorResponse::Retry);
assert!(!response.should_abort());
let fatal = MemScopeError::with_context(
ErrorKind::InternalError,
ErrorSeverity::Fatal,
"test",
"test",
);
let response = handler.determine_response(&fatal);
assert_eq!(response, ErrorResponse::Abort);
assert!(response.should_abort());
}
}