use std::borrow::Borrow;
use std::collections::hash_map::Entry::{Occupied, Vacant};
use std::collections::HashMap;
use std::hash::Hash;
#[derive(Debug)]
pub struct Cache<F, V>
where
F: FnOnce() -> V,
{
calculation_fn: Option<F>,
value: Option<V>,
}
impl<F, V> Cache<F, V>
where
F: FnOnce() -> V,
{
pub fn new(calculation_fn: F) -> Self {
Self {
calculation_fn: Some(calculation_fn),
value: None,
}
}
pub fn value_mut(&mut self) -> &mut V {
let calculation_fn = &mut self.calculation_fn;
self.value
.get_or_insert_with(|| (calculation_fn.take().unwrap())())
}
pub fn value(&self) -> Option<&V> {
self.value.as_ref()
}
}
#[derive(Debug)]
pub struct KeyedCache<F, K, V>
where
F: FnMut(&K) -> V,
K: Hash + Eq,
{
calculation_fn: F,
values: HashMap<K, V>,
}
impl<F, K, V> KeyedCache<F, K, V>
where
F: FnMut(&K) -> V,
K: Hash + Eq,
{
pub fn new(calculation_fn: F) -> Self {
Self {
calculation_fn,
values: HashMap::new(),
}
}
pub fn value_mut(&mut self, key: K) -> &mut V {
match self.values.entry(key) {
Vacant(e) => {
let calculation = (self.calculation_fn)(e.key());
e.insert(calculation)
}
Occupied(e) => e.into_mut(),
}
}
pub fn value<Q: ?Sized>(&self, key: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
self.values.get(key)
}
}
#[cfg(test)]
mod tests {
use crate::cache::{Cache, KeyedCache};
#[test]
fn cache_value_is_only_calculated_once() {
let mut counter = 0;
let mut sut = Cache::new(|| {
counter += 1;
});
let _ = sut.value_mut();
let _ = sut.value_mut();
assert_eq!(counter, 1);
}
#[test]
fn cache_value_is_correct() {
let mut sut = Cache::new(|| 42);
assert_eq!(42, *sut.value_mut());
}
#[test]
fn cache_value_is_not_available_before_access() {
let sut = Cache::new(|| 42);
assert_eq!(None, sut.value());
}
#[test]
fn cache_value_is_available_after_first_access() {
let mut sut = Cache::new(|| 42);
let _ = sut.value_mut();
assert_eq!(Some(&42), sut.value());
}
#[test]
fn cache_mut() {
let mut sut = Cache::new(|| 42);
let val = sut.value_mut();
assert_eq!(&mut 42, val);
*val = 5;
assert_eq!(5, *sut.value_mut());
}
#[test]
fn keyed_cache_value_is_only_calculated_once() {
let mut counter = 0;
let mut sut = KeyedCache::new(|x| {
counter += 1;
x + 5
});
let _ = sut.value_mut(5);
let _ = sut.value_mut(5);
let _ = sut.value_mut(10);
let _ = sut.value_mut(10);
assert_eq!(counter, 2);
}
#[test]
fn keyed_cache_value_is_correct() {
let mut sut = KeyedCache::new(|w: &String| {
let mut result = String::from("Hello ");
result.push_str(w);
result
});
assert_eq!("Hello World", sut.value_mut(String::from("World")));
}
#[test]
fn keyed_cache_value_is_not_available_before_access() {
let sut = KeyedCache::new(|_: &i32| 42);
assert_eq!(None, sut.value(&69));
}
#[test]
fn keyed_cache_value_is_available_after_first_access() {
let mut sut = KeyedCache::new(|k: &i32| *k + 42);
let _ = sut.value_mut(69);
assert_eq!(Some(&111), sut.value(&69));
}
}