use std::collections::{HashMap, VecDeque};
use std::sync::Mutex;
pub trait ResponseCache: Send + Sync {
fn get(&self, key: &str) -> Option<String>;
fn put(&self, key: String, value: String);
fn clear(&self);
}
#[derive(Default)]
pub struct MemoryCache {
inner: Mutex<CacheInner>,
}
struct CacheInner {
map: HashMap<String, String>,
order: VecDeque<String>,
max_entries: usize,
}
impl Default for CacheInner {
fn default() -> Self {
Self {
map: HashMap::new(),
order: VecDeque::new(),
max_entries: 256,
}
}
}
impl MemoryCache {
pub fn new() -> Self {
Self::default()
}
pub fn with_capacity(max_entries: usize) -> Self {
Self {
inner: Mutex::new(CacheInner {
max_entries: max_entries.max(1),
..Default::default()
}),
}
}
}
impl ResponseCache for MemoryCache {
fn get(&self, key: &str) -> Option<String> {
self.inner
.lock()
.ok()
.and_then(|inner| inner.map.get(key).cloned())
}
fn put(&self, key: String, value: String) {
if let Ok(mut inner) = self.inner.lock() {
if inner.map.insert(key.clone(), value).is_none() {
inner.order.push_back(key);
}
while inner.order.len() > inner.max_entries {
if let Some(oldest) = inner.order.pop_front() {
inner.map.remove(&oldest);
}
}
}
}
fn clear(&self) {
if let Ok(mut inner) = self.inner.lock() {
inner.map.clear();
inner.order.clear();
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cache_put_get() {
let cache = MemoryCache::new();
assert!(cache.get("k").is_none());
cache.put("k".to_string(), "v".to_string());
assert_eq!(cache.get("k").as_deref(), Some("v"));
}
#[test]
fn test_cache_evicts_oldest() {
let cache = MemoryCache::with_capacity(2);
cache.put("a".to_string(), "1".to_string());
cache.put("b".to_string(), "2".to_string());
cache.put("c".to_string(), "3".to_string());
assert!(cache.get("a").is_none(), "最旧条目应被淘汰");
assert_eq!(cache.get("b").as_deref(), Some("2"));
assert_eq!(cache.get("c").as_deref(), Some("3"));
}
#[test]
fn test_cache_clear() {
let cache = MemoryCache::new();
cache.put("a".to_string(), "1".to_string());
cache.clear();
assert!(cache.get("a").is_none());
}
}