use std::{
borrow::Borrow,
collections::HashMap,
error::Error,
hash::Hash,
ops::Index,
time::{Duration, Instant},
};
#[derive(Debug)]
pub struct Cache<K, V> {
hash_map: HashMap<K, (V, Instant)>,
expiry_duration: Option<Duration>,
}
impl<K, V> Cache<K, V>
where
K: Hash + Eq,
V: Clone + Copy,
{
pub fn keep_last() -> Self {
Cache {
hash_map: HashMap::new(),
expiry_duration: None,
}
}
pub fn with_expiry_duration(duration: Duration) -> Self {
Cache {
hash_map: HashMap::new(),
expiry_duration: Some(duration),
}
}
pub fn entry(&mut self, key: K) -> Entry<'_, K, V> {
match self.get(&key) {
Some(&v) => Entry::Occupied(OccupiedEntry { k: key, v }),
None => Entry::Vacant(VacantEntry {
k: key,
cache: self,
}),
}
}
pub fn entries<'a>(&'a mut self, keys: &'a [K]) -> Entries<'_, K, V> {
Entries { keys, cache: self }
}
fn has_expired(expiry_duration: Option<Duration>, t: &Instant) -> bool {
match expiry_duration {
Some(expiry) => t.elapsed() > expiry,
None => false,
}
}
pub fn get<Q: ?Sized>(&self, k: &Q) -> Option<&V>
where
K: Borrow<Q>,
Q: Hash + Eq,
{
match self.hash_map.get(k) {
Some((v, t)) => match Self::has_expired(self.expiry_duration, t) {
true => None,
false => Some(v),
},
None => None,
}
}
pub fn insert(&mut self, k: K, v: V) -> Option<V> {
self.hash_map.insert(k, (v, Instant::now())).map(|v| v.0)
}
}
impl<K, Q: ?Sized, V> Index<&Q> for Cache<K, V>
where
K: Eq + Hash + Borrow<Q>,
Q: Eq + Hash,
V: Clone + Copy,
{
type Output = V;
fn index(&self, index: &Q) -> &Self::Output {
self.get(index).expect("no entry found for key")
}
}
#[derive(Debug)]
pub enum Entry<'a, K: 'a, V: 'a> {
Occupied(OccupiedEntry<K, V>),
Vacant(VacantEntry<'a, K, V>),
}
impl<'a, K, V> Entry<'a, K, V>
where
K: Hash + Eq + Copy,
V: Clone + Copy,
{
pub fn or_insert(self, default: V) -> V {
match self {
Entry::Occupied(entry) => entry.v,
Entry::Vacant(entry) => entry.insert(default),
}
}
pub fn or_insert_with<F: FnOnce(&K) -> V>(self, default: F) -> V {
let k = *self.key();
match self {
Entry::Occupied(entry) => entry.v,
Entry::Vacant(entry) => entry.insert(default(&k)),
}
}
pub fn or_try_insert_with<F: FnOnce(&K) -> Result<V, Box<dyn Error>>>(
self,
default: F,
) -> Result<V, Box<dyn Error>> {
let k = *self.key();
match self {
Entry::Occupied(entry) => Ok(entry.v),
Entry::Vacant(entry) => match default(&k) {
Ok(v) => Ok(entry.insert(v)),
Err(e) => Err(e),
},
}
}
pub fn key(&self) -> &K {
match self {
Entry::Occupied(entry) => &entry.k,
Entry::Vacant(entry) => &entry.k,
}
}
}
#[derive(Debug)]
pub struct OccupiedEntry<K, V> {
k: K,
v: V,
}
#[derive(Debug)]
pub struct VacantEntry<'a, K, V> {
k: K,
cache: &'a mut Cache<K, V>,
}
impl<'a, K, V> VacantEntry<'a, K, V>
where
K: Hash + Eq + Clone,
V: Clone + Copy,
{
fn insert(self, v: V) -> V {
self.cache.insert(self.k.clone(), v);
*self.cache.get(&self.k).unwrap()
}
}
#[derive(Debug)]
pub struct Entries<'a, K: 'a, V: 'a> {
keys: &'a [K],
cache: &'a mut Cache<K, V>,
}
impl<'a, K, V> Entries<'a, K, V>
where
K: Hash + Eq + Copy,
V: Clone + Copy,
{
pub fn or_insert(self, default: V) -> Vec<V> {
let mut values = Vec::new();
for &k in self.keys {
match self.cache.get(&k) {
Some(v) => {
values.push(*v);
}
None => {
self.cache.insert(k, default);
values.push(default);
}
}
}
values
}
pub fn or_insert_with<F: FnOnce(&[K]) -> Vec<V>>(self, default: F) -> Vec<V> {
self.or_try_insert_with(|missing| Ok(default(missing)))
.unwrap()
}
pub fn or_try_insert_with<F: FnOnce(&[K]) -> Result<Vec<V>, Box<dyn Error>>>(
self,
default: F,
) -> Result<Vec<V>, Box<dyn Error>> {
let mut values = HashMap::new();
let mut missing = Vec::new();
for k in self.keys {
match self.cache.get(k) {
Some(&v) => {
values.insert(k, v);
}
None => {
missing.push(*k);
}
}
}
if !missing.is_empty() {
let missing_values = default(&missing)?;
assert_eq!(missing.len(), missing_values.len());
for (k, v) in missing.iter().zip(missing_values) {
self.cache.insert(*k, v);
values.insert(k, v);
}
}
Ok(self.keys.iter().map(|k| values[k]).collect())
}
}