use std::collections::HashMap;
use std::sync::{Arc, Mutex};
pub trait Cachable {
fn cache_id(&self) -> String;
}
type CacheInner<T> = Arc<Mutex<HashMap<String, Arc<T>>>>;
pub struct Cache<T>
where
T: Clone + Cachable,
{
inner: CacheInner<T>,
}
impl<T> Cache<T>
where
T: Clone + Cachable,
{
pub fn new() -> Arc<Self> {
Arc::new(Cache {
inner: Arc::new(Mutex::new(HashMap::new())),
})
}
pub fn set(&mut self, item: T) {
let mut cache = self.inner.lock().unwrap();
let cache_id = item.cache_id();
cache.entry(cache_id).or_insert_with(|| Arc::new(item));
}
pub fn add(&self, item: T) {
let mut cache = self.inner.lock().unwrap();
let cache_id = item.cache_id();
cache.entry(cache_id).or_insert_with(|| Arc::new(item));
}
pub fn get<I>(&self, cache_id: I) -> Option<Arc<T>>
where
I: AsRef<str>,
{
let cache = self.inner.lock().unwrap();
let id = cache_id.as_ref();
cache.get(id).cloned()
}
pub fn value(&mut self, item: T) -> Arc<T> {
let mut cache = self.inner.lock().unwrap();
let entry = cache.entry(item.cache_id());
entry.or_insert_with(|| item.into()).to_owned()
}
pub fn clear(&self) {
let mut cache = self.inner.lock().unwrap();
cache.clear();
}
pub fn remove<I>(&self, cache_id: I)
where
I: AsRef<str>,
{
let mut cache = self.inner.lock().unwrap();
let id = cache_id.as_ref();
cache.remove(id);
}
pub fn get_all(&self) -> Vec<Arc<T>> {
let cache = self.inner.lock().unwrap();
cache.values().cloned().collect()
}
pub fn is_empty(&self) -> bool {
let cache = self.inner.lock().unwrap();
cache.is_empty()
}
pub fn size(&self) -> usize {
let cache = self.inner.lock().unwrap();
cache.len()
}
pub fn contains<I>(&self, cache_id: I) -> bool
where
I: AsRef<str>,
{
let cache = self.inner.lock().unwrap();
let id = cache_id.as_ref();
cache.contains_key(id)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Clone)]
struct TemplateItem {
pub template: String,
pub extension: String,
}
impl Cachable for TemplateItem {
fn cache_id(&self) -> String {
self.extension.clone()
}
}
#[test]
fn test_cache_add_and_get_template() {
let cache = Cache::<TemplateItem>::new();
let extension = ".rs";
let template = "Your license template here";
cache.add(TemplateItem {
extension: extension.into(),
template: template.into(),
});
let cached_template = cache.get(extension);
assert!(cached_template.is_some());
assert_eq!(cached_template.unwrap().template, template);
}
#[test]
fn test_cache_get_template_not_found() {
let cache = Cache::<TemplateItem>::new();
let extension = ".rs";
let cached_template = cache.get(extension);
assert!(cached_template.is_none());
}
#[test]
fn test_cache_remove_template() {
let cache = Cache::<TemplateItem>::new();
let extension = ".rs";
let template = "Your license template here";
cache.add(TemplateItem {
extension: extension.into(),
template: template.into(),
});
cache.remove(extension);
let cached_template = cache.get(extension);
assert!(cached_template.is_none());
}
#[test]
fn test_cache_clear() {
let cache = Cache::<TemplateItem>::new();
let extension = ".rs";
let template = "Your license template here";
cache.add(TemplateItem {
extension: extension.into(),
template: template.into(),
});
cache.clear();
assert!(cache.is_empty());
}
#[test]
fn test_cache_get_all() {
let cache = Cache::<TemplateItem>::new();
let ext1 = ".rs";
let ext2 = ".toml";
let template1 = "Your license template for Rust";
let template2 = "Your license template for TOML";
cache.add(TemplateItem {
extension: ext1.into(),
template: template1.into(),
});
cache.add(TemplateItem {
extension: ext2.into(),
template: template2.into(),
});
let all_templates = cache.get_all();
assert_eq!(all_templates.len(), 2);
}
#[test]
fn test_cache_size() {
let cache = Cache::<TemplateItem>::new();
let ext1 = ".rs";
let ext2 = ".toml";
let template1 = "Your license template for Rust";
let template2 = "Your license template for TOML";
cache.add(TemplateItem {
extension: ext1.into(),
template: template1.into(),
});
cache.add(TemplateItem {
extension: ext2.into(),
template: template2.into(),
});
let size = cache.size();
assert_eq!(size, 2);
}
#[test]
fn test_cache_contains() {
let cache = Cache::<TemplateItem>::new();
let ext1 = ".rs";
let template1 = "Your license template for Rust";
cache.add(TemplateItem {
extension: ext1.into(),
template: template1.into(),
});
assert!(cache.contains(ext1));
assert!(!cache.contains(".toml"));
}
}