#![allow(dead_code)]
use std::sync::RwLock;
use lru_cache::LruCache;
pub struct Cache<Src: 'static + CacheSource> {
cache: RwLock<LruCache<String, Src::Value>>,
src: Src,
}
impl<Src: 'static + CacheSource> Cache<Src> {
pub fn new(capacity: usize, src: Src) -> Cache<Src> {
Cache {
cache: RwLock::new(LruCache::new(capacity)),
src: src,
}
}
fn fetch(&self, id: &str) -> Option<Src::Value> {
let guard = self.cache.read().unwrap();
if let Some((_, val)) = guard.iter().find(|x| x.0 == id) {
Some(val.clone())
} else { None }
}
pub fn get(&self, id: &str) -> Option<Src::Value> {
let cached = self.fetch(id);
if cached.is_some() { return cached }
if let Some(new) = self.src.generate(id) {
let mut guard = self.cache.write().unwrap();
guard.insert(id.to_string(), new);
} else { return None }
self.fetch(id)
}
pub fn insert(&self, id: &str, new_val: Src::Value) -> Option<Src::Value> {
let mut guard = self.cache.write().unwrap();
if let Some(mut old_val) = guard.insert(id.to_string(), new_val) {
self.src.dispose(id, &mut old_val);
Some(old_val)
} else { None }
}
pub fn remove(&self, id: &str) -> Option<Src::Value> {
let mut guard = self.cache.write().unwrap();
if let Some(mut old_val) = guard.remove(id) {
self.src.remove(id, &mut old_val);
Some(old_val)
} else { None }
}
pub fn capacity(&self) -> usize {
self.cache.read().unwrap().capacity()
}
pub fn len(&self) -> usize {
self.cache.read().unwrap().len()
}
}
pub trait CacheSource: 'static + Send + Sync {
type Value: Clone;
fn generate(&self, id: &str) -> Option<Self::Value>;
fn dispose(&self, _id: &str, _obj: &mut Self::Value) {}
fn remove(&self, _id: &str, _obj: &mut Self::Value) {}
}
#[cfg(test)]
mod tests {
struct TestSource(bool);
impl super::CacheSource for TestSource {
type Value = &'static str;
fn generate(&self, id: &str) -> Option<Self::Value> {
if self.0 { None }
else { Some(&["cache0", "cache1", "cache2", "cache3"][id.parse::<usize>().unwrap()]) }
}
fn dispose(&self, _id: &str, obj: &mut Self::Value) {
*obj = "disposed"
}
fn remove(&self, _id: &str, obj: &mut Self::Value) {
*obj = "removed"
}
}
type TestCache = super::Cache<TestSource>;
fn make_cache(fail: bool) -> TestCache {
TestCache::new(3, TestSource(fail))
}
#[test]
fn test_cache() {
let cache = make_cache(false);
assert_eq!(cache.get("0"), Some("cache0"));
assert_eq!(cache.get("1"), Some("cache1"));
assert_eq!(cache.get("2"), Some("cache2"));
}
#[test]
fn test_cache_failure() {
let cache = make_cache(true);
assert_eq!(cache.get("0"), None);
assert_eq!(cache.get("1"), None);
assert_eq!(cache.get("2"), None);
}
#[test]
fn test_max_cache() {
let cache = make_cache(false);
assert_eq!(cache.len(), 0);
assert_eq!(cache.get("0"), Some("cache0"));
assert_eq!(cache.len(), 1);
assert_eq!(cache.get("1"), Some("cache1"));
assert_eq!(cache.len(), 2);
assert_eq!(cache.get("2"), Some("cache2"));
assert_eq!(cache.len(), 3);
assert_eq!(cache.get("3"), Some("cache3"));
assert_eq!(cache.len(), 3);
}
#[test]
fn test_max_cache_failure() {
let cache = make_cache(true);
assert_eq!(cache.len(), 0);
assert_eq!(cache.get("0"), None);
assert_eq!(cache.len(), 0);
cache.get("1");
assert_eq!(cache.len(), 0);
cache.get("2");
assert_eq!(cache.len(), 0);
}
#[test]
fn test_remove() {
let cache = make_cache(false);
cache.get("0");
assert_eq!(cache.len(), 1);
assert_eq!(cache.remove("0"), Some("removed"));
assert_eq!(cache.len(), 0);
}
}