use std::fmt;
#[cfg(feature = "redis")]
#[derive(Debug)]
pub enum OxCacheConfigError {
MissingField(String),
InvalidValue { field: String, reason: String },
UnsupportedBackend(String),
ConnectionFailed(String),
}
#[cfg(feature = "redis")]
impl fmt::Display for OxCacheConfigError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let locale = crate::i18n::get_default_locale();
f.write_str(&self.localized_message(&locale))
}
}
#[cfg(feature = "redis")]
impl std::error::Error for OxCacheConfigError {}
#[cfg(feature = "redis")]
impl OxCacheConfigError {
pub fn message_id(&self) -> &'static str {
match self {
OxCacheConfigError::MissingField(_) => crate::i18n::messages::MSG_CFG_MISSING_FIELD,
OxCacheConfigError::InvalidValue { .. } => crate::i18n::messages::MSG_CFG_INVALID_VALUE,
OxCacheConfigError::UnsupportedBackend(_) => crate::i18n::messages::MSG_CFG_UNSUPPORTED_BACKEND,
OxCacheConfigError::ConnectionFailed(_) => crate::i18n::messages::MSG_CFG_CONNECTION_FAILED,
}
}
pub fn localized_message(&self, locale: &str) -> String {
let params: Vec<(&str, String)> = match self {
OxCacheConfigError::MissingField(f) => vec![("field", f.clone())],
OxCacheConfigError::InvalidValue { field, reason } => {
vec![("field", field.clone()), ("reason", reason.clone())]
}
OxCacheConfigError::UnsupportedBackend(d) => vec![("detail", d.clone())],
OxCacheConfigError::ConnectionFailed(d) => vec![("detail", d.clone())],
};
let template = crate::i18n::messages::lookup(locale, self.message_id()).unwrap_or(self.message_id());
let borrowed: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
crate::i18n::messages::format_template(template, &borrowed)
}
}
#[cfg(feature = "redis")]
pub type OxCacheConfigResult<T> = std::result::Result<T, OxCacheConfigError>;
#[derive(Debug)]
pub enum OxCacheError {
Serialization(String),
Operation(String),
Connection(String),
NotFound(String),
Degraded(String),
L1Error(String),
L2Error(String),
NotSupported(String),
WalError(String),
DatabaseError(String),
#[cfg(feature = "redis")]
RedisError(redis::RedisError),
#[cfg(not(feature = "redis"))]
RedisError(String),
IoError(std::io::Error),
BackendError(String),
Timeout(String),
ShutdownError(String),
KeyTooLong(usize, usize),
ValueTooLarge(usize, usize),
BufferFull(String),
InvalidInput(String),
InvalidKey(String),
LockError(String),
ServiceNotFound(String),
Internal(String),
}
impl fmt::Display for OxCacheError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let locale = crate::i18n::get_default_locale();
f.write_str(&self.localized_message(&locale))
}
}
impl std::error::Error for OxCacheError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
#[cfg(feature = "redis")]
OxCacheError::RedisError(e) => Some(e),
OxCacheError::IoError(e) => Some(e),
_ => None,
}
}
}
pub type OxCacheResult<T> = std::result::Result<T, OxCacheError>;
impl From<std::io::Error> for OxCacheError {
fn from(e: std::io::Error) -> Self {
OxCacheError::IoError(e)
}
}
#[cfg(feature = "redis")]
impl From<redis::RedisError> for OxCacheError {
fn from(e: redis::RedisError) -> Self {
OxCacheError::RedisError(e)
}
}
#[cfg(any(feature = "serialization", feature = "full"))]
impl From<serde_json::Error> for OxCacheError {
fn from(e: serde_json::Error) -> Self {
OxCacheError::Serialization(e.to_string())
}
}
impl OxCacheError {
pub fn code(&self) -> &'static str {
match self {
OxCacheError::NotFound(_) => "OXCACHE_001",
OxCacheError::Connection(_) => "OXCACHE_002",
OxCacheError::Serialization(_) => "OXCACHE_003",
OxCacheError::Operation(_) => "OXCACHE_004",
OxCacheError::Degraded(_) => "OXCACHE_005",
OxCacheError::L1Error(_) => "OXCACHE_006",
OxCacheError::L2Error(_) => "OXCACHE_007",
OxCacheError::NotSupported(_) => "OXCACHE_009",
OxCacheError::WalError(_) => "OXCACHE_010",
OxCacheError::DatabaseError(_) => "OXCACHE_011",
OxCacheError::RedisError(_) => "OXCACHE_012",
OxCacheError::IoError(_) => "OXCACHE_013",
OxCacheError::BackendError(_) => "OXCACHE_014",
OxCacheError::Timeout(_) => "OXCACHE_015",
OxCacheError::ShutdownError(_) => "OXCACHE_016",
OxCacheError::KeyTooLong(_, _) => "OXCACHE_017",
OxCacheError::ValueTooLarge(_, _) => "OXCACHE_018",
OxCacheError::BufferFull(_) => "OXCACHE_019",
OxCacheError::InvalidInput(_) => "OXCACHE_020",
OxCacheError::InvalidKey(_) => "OXCACHE_021",
OxCacheError::LockError(_) => "OXCACHE_022",
OxCacheError::ServiceNotFound(_) => "OXCACHE_023",
OxCacheError::Internal(_) => "OXCACHE_024",
}
}
pub fn is_recoverable(&self) -> bool {
matches!(
self,
OxCacheError::Connection(_)
| OxCacheError::Timeout(_)
| OxCacheError::RedisError(_)
| OxCacheError::L2Error(_)
| OxCacheError::BackendError(_)
| OxCacheError::BufferFull(_)
)
}
pub fn is_not_found(&self) -> bool {
matches!(self, OxCacheError::NotFound(_))
}
pub fn is_connection_error(&self) -> bool {
matches!(
self,
OxCacheError::Connection(_) | OxCacheError::RedisError(_) | OxCacheError::L2Error(_)
)
}
pub fn is_degraded(&self) -> bool {
matches!(self, OxCacheError::Degraded(_))
}
pub fn message_id(&self) -> &'static str {
match self {
OxCacheError::Serialization(_) => crate::i18n::messages::MSG_ERR_SERIALIZATION,
OxCacheError::Operation(_) => crate::i18n::messages::MSG_ERR_OPERATION,
OxCacheError::Connection(_) => crate::i18n::messages::MSG_ERR_CONNECTION,
OxCacheError::NotFound(_) => crate::i18n::messages::MSG_ERR_NOT_FOUND,
OxCacheError::Degraded(_) => crate::i18n::messages::MSG_ERR_DEGRADED,
OxCacheError::L1Error(_) => crate::i18n::messages::MSG_ERR_L1,
OxCacheError::L2Error(_) => crate::i18n::messages::MSG_ERR_L2,
OxCacheError::NotSupported(_) => crate::i18n::messages::MSG_ERR_NOT_SUPPORTED,
OxCacheError::WalError(_) => crate::i18n::messages::MSG_ERR_WAL,
OxCacheError::DatabaseError(_) => crate::i18n::messages::MSG_ERR_DATABASE,
OxCacheError::RedisError(_) => crate::i18n::messages::MSG_ERR_REDIS,
OxCacheError::IoError(_) => crate::i18n::messages::MSG_ERR_IO,
OxCacheError::BackendError(_) => crate::i18n::messages::MSG_ERR_BACKEND,
OxCacheError::Timeout(_) => crate::i18n::messages::MSG_ERR_TIMEOUT,
OxCacheError::ShutdownError(_) => crate::i18n::messages::MSG_ERR_SHUTDOWN,
OxCacheError::KeyTooLong(_, _) => crate::i18n::messages::MSG_ERR_KEY_TOO_LONG,
OxCacheError::ValueTooLarge(_, _) => crate::i18n::messages::MSG_ERR_VALUE_TOO_LARGE,
OxCacheError::BufferFull(_) => crate::i18n::messages::MSG_ERR_BUFFER_FULL,
OxCacheError::InvalidInput(_) => crate::i18n::messages::MSG_ERR_INVALID_INPUT,
OxCacheError::InvalidKey(_) => crate::i18n::messages::MSG_ERR_INVALID_KEY,
OxCacheError::LockError(_) => crate::i18n::messages::MSG_ERR_LOCK,
OxCacheError::ServiceNotFound(_) => crate::i18n::messages::MSG_ERR_SERVICE_NOT_FOUND,
OxCacheError::Internal(_) => crate::i18n::messages::MSG_ERR_INTERNAL,
}
}
pub fn localized_message(&self, locale: &str) -> String {
let params = self.message_params();
let template = crate::i18n::messages::lookup(locale, self.message_id()).unwrap_or(self.message_id());
let borrowed: Vec<(&str, &str)> = params.iter().map(|(k, v)| (*k, v.as_str())).collect();
crate::i18n::messages::format_template(template, &borrowed)
}
fn message_params(&self) -> Vec<(&str, String)> {
match self {
OxCacheError::KeyTooLong(actual, max) => vec![("actual", actual.to_string()), ("max", max.to_string())],
OxCacheError::ValueTooLarge(actual, max) => vec![("actual", actual.to_string()), ("max", max.to_string())],
OxCacheError::Serialization(d)
| OxCacheError::Operation(d)
| OxCacheError::Connection(d)
| OxCacheError::NotFound(d)
| OxCacheError::Degraded(d)
| OxCacheError::L1Error(d)
| OxCacheError::L2Error(d)
| OxCacheError::NotSupported(d)
| OxCacheError::WalError(d)
| OxCacheError::DatabaseError(d)
| OxCacheError::BackendError(d)
| OxCacheError::Timeout(d)
| OxCacheError::ShutdownError(d)
| OxCacheError::BufferFull(d)
| OxCacheError::InvalidInput(d)
| OxCacheError::InvalidKey(d)
| OxCacheError::LockError(d)
| OxCacheError::ServiceNotFound(d)
| OxCacheError::Internal(d) => vec![("detail", d.clone())],
OxCacheError::RedisError(e) => vec![("detail", e.to_string())],
OxCacheError::IoError(e) => vec![("detail", e.to_string())],
}
}
}
#[cfg(test)]
mod tests;