use crate::error::Result;
use async_trait::async_trait;
use std::time::Duration;
#[async_trait]
pub trait CacheBackend: Send + Sync {
async fn get(&self, key: &str) -> Result<Option<Vec<u8>>>;
async fn set(&self, key: &str, value: Vec<u8>, ttl: Option<Duration>) -> Result<()>;
async fn delete(&self, key: &str) -> Result<()>;
async fn exists(&self, key: &str) -> Result<bool>;
async fn clear(&self) -> Result<()>;
async fn close(&self) -> Result<()>;
async fn ttl(&self, key: &str) -> Result<Option<Duration>>;
async fn expire(&self, key: &str, ttl: Duration) -> Result<bool>;
async fn health_check(&self) -> Result<bool>;
async fn stats(&self) -> Result<std::collections::HashMap<String, String>>;
}
#[cfg(test)]
mod tests {
use super::*;
use std::collections::HashMap;
struct MockBackend {
data: std::sync::Arc<tokio::sync::RwLock<HashMap<String, Vec<u8>>>>,
}
impl MockBackend {
fn new() -> Self {
Self {
data: std::sync::Arc::new(tokio::sync::RwLock::new(HashMap::new())),
}
}
}
#[async_trait]
impl CacheBackend for MockBackend {
async fn get(&self, key: &str) -> Result<Option<Vec<u8>>> {
let data = self.data.read().await;
Ok(data.get(key).cloned())
}
async fn set(&self, key: &str, value: Vec<u8>, _ttl: Option<Duration>) -> Result<()> {
let mut data = self.data.write().await;
data.insert(key.to_string(), value);
Ok(())
}
async fn delete(&self, key: &str) -> Result<()> {
let mut data = self.data.write().await;
data.remove(key);
Ok(())
}
async fn exists(&self, key: &str) -> Result<bool> {
let data = self.data.read().await;
Ok(data.contains_key(key))
}
async fn clear(&self) -> Result<()> {
let mut data = self.data.write().await;
data.clear();
Ok(())
}
async fn close(&self) -> Result<()> {
Ok(())
}
async fn ttl(&self, _key: &str) -> Result<Option<Duration>> {
Ok(None)
}
async fn expire(&self, _key: &str, _ttl: Duration) -> Result<bool> {
Ok(false)
}
async fn health_check(&self) -> Result<bool> {
Ok(true)
}
async fn stats(&self) -> Result<HashMap<String, String>> {
let mut stats = HashMap::new();
stats.insert("type".to_string(), "mock".to_string());
Ok(stats)
}
}
#[tokio::test]
async fn test_mock_backend() {
let backend = MockBackend::new();
backend.set("key1", b"value1".to_vec(), None).await.unwrap();
let value = backend.get("key1").await.unwrap();
assert_eq!(value, Some(b"value1".to_vec()));
assert!(backend.exists("key1").await.unwrap());
assert!(!backend.exists("key2").await.unwrap());
backend.delete("key1").await.unwrap();
assert!(!backend.exists("key1").await.unwrap());
assert!(backend.health_check().await.unwrap());
let stats = backend.stats().await.unwrap();
assert_eq!(stats.get("type"), Some(&"mock".to_string()));
}
}