use std::io;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum CacheError {
#[error("Cache I/O error: {0}")]
Io(#[from] io::Error),
#[error("Cache serialization error: {0}")]
Serialization(#[from] serde_json::Error),
#[error("Cache capacity exceeded: {current} >= {max}")]
CapacityExceeded { current: usize, max: usize },
#[error("Invalid cache configuration: {0}")]
InvalidConfig(String),
#[error("Cache entry not found: {key}")]
EntryNotFound { key: String },
#[error("Cache entry expired: {key}")]
EntryExpired { key: String },
#[error("Cache operation timeout after {timeout_ms}ms")]
Timeout { timeout_ms: u64 },
#[error("Cache corruption detected: {details}")]
Corruption { details: String },
#[error("Insufficient memory: requested {requested} bytes, available {available} bytes")]
InsufficientMemory { requested: u64, available: u64 },
#[error("Cache lock contention: operation failed after {attempts} attempts")]
LockContention { attempts: u32 },
#[error("Preheating error: {0}")]
PreheatingError(String),
#[error("Tuning error: {0}")]
TuningError(String),
#[error("Cache level {level} error: {message}")]
CacheLevelError { level: String, message: String },
#[error("Cache error: {0}")]
Generic(String),
}
impl CacheError {
pub fn capacity_exceeded(current: usize, max: usize) -> Self {
Self::CapacityExceeded { current, max }
}
pub fn invalid_config<S: Into<String>>(message: S) -> Self {
Self::InvalidConfig(message.into())
}
pub fn entry_not_found<S: Into<String>>(key: S) -> Self {
Self::EntryNotFound { key: key.into() }
}
pub fn entry_expired<S: Into<String>>(key: S) -> Self {
Self::EntryExpired { key: key.into() }
}
pub fn timeout(timeout_ms: u64) -> Self {
Self::Timeout { timeout_ms }
}
pub fn corruption<S: Into<String>>(details: S) -> Self {
Self::Corruption {
details: details.into(),
}
}
pub fn insufficient_memory(requested: u64, available: u64) -> Self {
Self::InsufficientMemory {
requested,
available,
}
}
pub fn lock_contention(attempts: u32) -> Self {
Self::LockContention { attempts }
}
pub fn preheating_error<S: Into<String>>(message: S) -> Self {
Self::PreheatingError(message.into())
}
pub fn tuning_error<S: Into<String>>(message: S) -> Self {
Self::TuningError(message.into())
}
pub fn cache_level_error<S: Into<String>>(level: S, message: S) -> Self {
Self::CacheLevelError {
level: level.into(),
message: message.into(),
}
}
pub fn generic<S: Into<String>>(message: S) -> Self {
Self::Generic(message.into())
}
pub fn is_recoverable(&self) -> bool {
match self {
CacheError::Io(_) => false,
CacheError::Serialization(_) => false,
CacheError::CapacityExceeded { .. } => true,
CacheError::InvalidConfig(_) => false,
CacheError::EntryNotFound { .. } => true,
CacheError::EntryExpired { .. } => true,
CacheError::Timeout { .. } => true,
CacheError::Corruption { .. } => false,
CacheError::InsufficientMemory { .. } => true,
CacheError::LockContention { .. } => true,
CacheError::PreheatingError(_) => true,
CacheError::TuningError(_) => true,
CacheError::CacheLevelError { .. } => true,
CacheError::Generic(_) => false,
}
}
pub fn severity(&self) -> ErrorSeverity {
match self {
CacheError::Io(_) => ErrorSeverity::Critical,
CacheError::Serialization(_) => ErrorSeverity::High,
CacheError::CapacityExceeded { .. } => ErrorSeverity::Medium,
CacheError::InvalidConfig(_) => ErrorSeverity::High,
CacheError::EntryNotFound { .. } => ErrorSeverity::Low,
CacheError::EntryExpired { .. } => ErrorSeverity::Low,
CacheError::Timeout { .. } => ErrorSeverity::Medium,
CacheError::Corruption { .. } => ErrorSeverity::Critical,
CacheError::InsufficientMemory { .. } => ErrorSeverity::High,
CacheError::LockContention { .. } => ErrorSeverity::Medium,
CacheError::PreheatingError(_) => ErrorSeverity::Low,
CacheError::TuningError(_) => ErrorSeverity::Low,
CacheError::CacheLevelError { .. } => ErrorSeverity::Medium,
CacheError::Generic(_) => ErrorSeverity::Medium,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum ErrorSeverity {
Low,
Medium,
High,
Critical,
}
impl ErrorSeverity {
pub fn as_str(&self) -> &'static str {
match self {
ErrorSeverity::Low => "LOW",
ErrorSeverity::Medium => "MEDIUM",
ErrorSeverity::High => "HIGH",
ErrorSeverity::Critical => "CRITICAL",
}
}
pub fn requires_immediate_attention(&self) -> bool {
matches!(self, ErrorSeverity::High | ErrorSeverity::Critical)
}
}
pub type CacheResult<T> = Result<T, CacheError>;
#[derive(Debug, Clone)]
pub struct ErrorContext {
pub operation: String,
pub cache_level: Option<String>,
pub context: std::collections::HashMap<String, String>,
pub timestamp: u64,
}
impl ErrorContext {
pub fn new<S: Into<String>>(operation: S) -> Self {
Self {
operation: operation.into(),
cache_level: None,
context: std::collections::HashMap::new(),
timestamp: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map(|d| d.as_secs())
.unwrap_or(0),
}
}
pub fn with_cache_level<S: Into<String>>(mut self, level: S) -> Self {
self.cache_level = Some(level.into());
self
}
pub fn with_context<K: Into<String>, V: Into<String>>(mut self, key: K, value: V) -> Self {
self.context.insert(key.into(), value.into());
self
}
}
#[derive(Debug)]
pub struct ContextualCacheError {
pub error: CacheError,
pub context: ErrorContext,
}
impl ContextualCacheError {
pub fn new(error: CacheError, context: ErrorContext) -> Self {
Self { error, context }
}
pub fn severity(&self) -> ErrorSeverity {
self.error.severity()
}
pub fn is_recoverable(&self) -> bool {
self.error.is_recoverable()
}
}
impl std::fmt::Display for ContextualCacheError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"Cache error in operation '{}': {} (severity: {})",
self.context.operation,
self.error,
self.severity().as_str()
)?;
if let Some(ref level) = self.context.cache_level {
write!(f, " [cache level: {}]", level)?;
}
if !self.context.context.is_empty() {
write!(f, " [context: {:?}]", self.context.context)?;
}
Ok(())
}
}
impl std::error::Error for ContextualCacheError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.error)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_error_creation() {
let error = CacheError::capacity_exceeded(100, 50);
assert!(matches!(
error,
CacheError::CapacityExceeded {
current: 100,
max: 50
}
));
assert!(error.is_recoverable());
assert_eq!(error.severity(), ErrorSeverity::Medium);
}
#[test]
fn test_error_context() {
let context = ErrorContext::new("get_operation")
.with_cache_level("L1")
.with_context("key", "test_key");
assert_eq!(context.operation, "get_operation");
assert_eq!(context.cache_level, Some("L1".to_string()));
assert_eq!(context.context.get("key"), Some(&"test_key".to_string()));
}
#[test]
fn test_contextual_error() {
let error = CacheError::timeout(1000);
let context = ErrorContext::new("put_operation");
let contextual_error = ContextualCacheError::new(error, context);
assert_eq!(contextual_error.severity(), ErrorSeverity::Medium);
assert!(contextual_error.is_recoverable());
}
#[test]
fn test_error_severity() {
assert!(ErrorSeverity::Critical.requires_immediate_attention());
assert!(ErrorSeverity::High.requires_immediate_attention());
assert!(!ErrorSeverity::Medium.requires_immediate_attention());
assert!(!ErrorSeverity::Low.requires_immediate_attention());
}
}