use std::{future::Future, pin::Pin, time::Duration};
use serde::{de::DeserializeOwned, Serialize};
pub trait Cache: Send + Sync {
fn get<T: DeserializeOwned + Send>(
&self,
key: &str,
) -> Pin<Box<dyn Future<Output = Option<T>> + Send + '_>>;
fn set<T: Serialize + Send + Sync>(
&self,
key: &str,
value: &T,
ttl: Option<Duration>,
) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
fn delete(&self, key: &str) -> Pin<Box<dyn Future<Output = bool> + Send + '_>>;
fn exists(&self, key: &str) -> Pin<Box<dyn Future<Output = bool> + Send + '_>>;
fn get_many<T: DeserializeOwned + Send>(
&self,
keys: &[&str],
) -> Pin<Box<dyn Future<Output = Vec<Option<T>>> + Send + '_>> {
let keys: Vec<String> = keys.iter().map(|k| k.to_string()).collect();
Box::pin(async move {
let mut results = Vec::with_capacity(keys.len());
for key in keys {
results.push(self.get(&key).await);
}
results
})
}
fn set_many<'a, T: Serialize + Send + Sync + 'a>(
&'a self,
entries: Vec<(String, T)>,
ttl: Option<Duration>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
Box::pin(async move {
for (key, value) in entries {
self.set(&key, &value, ttl).await;
}
})
}
fn delete_many(&self, keys: &[&str]) -> Pin<Box<dyn Future<Output = usize> + Send + '_>> {
let keys: Vec<String> = keys.iter().map(|k| k.to_string()).collect();
Box::pin(async move {
let mut count = 0;
for key in keys {
if self.delete(&key).await {
count += 1;
}
}
count
})
}
fn clear(&self) -> Pin<Box<dyn Future<Output = ()> + Send + '_>>;
fn len(&self) -> Pin<Box<dyn Future<Output = Option<usize>> + Send + '_>> {
Box::pin(async { None })
}
fn is_empty(&self) -> Pin<Box<dyn Future<Output = bool> + Send + '_>> {
Box::pin(async move { self.len().await.map(|n| n == 0).unwrap_or(true) })
}
}
pub trait CacheExt: Cache {
fn get_keyed<K: super::CacheKey, T: DeserializeOwned + Send>(
&self,
key: &K,
) -> Pin<Box<dyn Future<Output = Option<T>> + Send + '_>> {
let key = key.cache_key();
Box::pin(async move { self.get(&key).await })
}
fn set_keyed<'a, K: super::CacheKey, T: Serialize + Send + Sync + 'a>(
&'a self,
key: &K,
value: T,
ttl: Option<Duration>,
) -> Pin<Box<dyn Future<Output = ()> + Send + 'a>> {
let key = key.cache_key();
Box::pin(async move { self.set(&key, &value, ttl).await })
}
fn delete_keyed<K: super::CacheKey>(
&self,
key: &K,
) -> Pin<Box<dyn Future<Output = bool> + Send + '_>> {
let key = key.cache_key();
Box::pin(async move { self.delete(&key).await })
}
}
impl<C: Cache + ?Sized> CacheExt for C {}
pub type CacheResult<T> = Result<T, CacheError>;
#[derive(Debug, Clone)]
pub enum CacheError {
Serialization(String),
Deserialization(String),
Connection(String),
Timeout,
Other(String),
}
impl std::fmt::Display for CacheError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CacheError::Serialization(msg) => write!(f, "Serialization error: {}", msg),
CacheError::Deserialization(msg) => write!(f, "Deserialization error: {}", msg),
CacheError::Connection(msg) => write!(f, "Connection error: {}", msg),
CacheError::Timeout => write!(f, "Operation timed out"),
CacheError::Other(msg) => write!(f, "Cache error: {}", msg),
}
}
}
impl std::error::Error for CacheError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_error_display() {
let err = CacheError::Connection("refused".into());
assert!(err.to_string().contains("refused"));
}
}