use redis::RedisError; pub type CacheResult = Result; #[derive(Debug)] pub enum CacheError { Config(String), Backend(RedisError), Encode { key: String, message: String, }, Decode { key: String, message: String, }, Unsupported { feature: &'static str, reason: &'static str, }, Lock { key: String, message: String, }, } impl CacheError { pub fn config(message: impl Into) -> Self { Self::Config(message.into()) } pub fn encode(key: impl Into, error: serde_json::Error) -> Self { Self::Encode { key: key.into(), message: error.to_string(), } } pub fn decode(key: impl Into, error: serde_json::Error) -> Self { Self::Decode { key: key.into(), message: error.to_string(), } } pub fn unsupported(feature: &'static str, reason: &'static str) -> Self { Self::Unsupported { feature, reason } } pub fn lock(key: impl Into, message: impl Into) -> Self { Self::Lock { key: key.into(), message: message.into(), } } pub fn key(&self) -> Option<&str> { match self { Self::Encode { key, .. } | Self::Decode { key, .. } | Self::Lock { key, .. } => { Some(key) } _ => None, } } pub fn is_config(&self) -> bool { matches!(self, Self::Config(_)) } pub fn is_closed(&self) -> bool { match self { Self::Backend(error) => error.is_connection_dropped(), _ => false, } } pub fn is_retryable(&self) -> bool { match self { Self::Backend(error) => { error.is_io_error() || error.is_timeout() || error.is_connection_refusal() || error.is_connection_dropped() || error.is_cluster_error() } _ => false, } } } impl std::fmt::Display for CacheError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Config(message) => write!(f, "cache config: {message}"), Self::Backend(error) => write!(f, "cache backend: {error}"), Self::Encode { key, message } => write!(f, "cache encode {key}: {message}"), Self::Decode { key, message } => write!(f, "cache decode {key}: {message}"), Self::Unsupported { feature, reason } => { write!(f, "cache unsupported {feature}: {reason}") } Self::Lock { key, message } => write!(f, "cache lock {key}: {message}"), } } } impl std::error::Error for CacheError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { Self::Backend(error) => Some(error), _ => None, } } } impl From for CacheError { fn from(error: RedisError) -> Self { Self::Backend(error) } }