use crate::{
CacheCapacity, CacheKey, CacheStrategy, CompressionStrategy, FlushableStrategy,
RecoverableStrategy, Result,
};
use std::{borrow::Cow, collections::HashMap, hash::Hash};
#[derive(Debug)]
pub struct Cache<K, S, C>
where
K: CacheKey + Eq + Hash,
S: CacheStrategy,
C: CompressionStrategy + Sync + Send,
{
data: HashMap<K, S::CacheEntry>,
strategy: S,
compressor: Option<C>,
}
impl<K, S, C> Cache<K, S, C>
where
K: CacheKey + Eq + Hash + Sync + Send,
S: CacheStrategy + Send,
C: CompressionStrategy + Sync + Send,
{
pub async fn new(mut strategy: S, compressor: Option<C>) -> Result<Cache<K, S, C>>
where
C: CompressionStrategy + Sync + Send,
{
strategy.setup().await?;
Ok(Cache {
data: HashMap::new(),
strategy,
compressor,
})
}
pub async fn put<'a, V>(&mut self, key: K, value: V) -> Result<()>
where
V: Into<Cow<'a, [u8]>> + Send,
{
let value: Cow<'_, [u8]> = self.compressor.compress(value.into()).await?;
let entry = self.strategy.put(&key, value).await?;
self.data.insert(key, entry);
Ok(())
}
pub async fn get(&self, key: K) -> Result<Cow<'_, [u8]>> {
let entry = self.data.get(&key).ok_or(crate::Error::KeyNotFound)?;
let value = self.strategy.get(entry).await?;
self.compressor.decompress(value).await
}
pub async fn take(&mut self, key: K) -> Result<Vec<u8>> {
let entry = self.data.remove(&key).ok_or(crate::Error::KeyNotFound)?;
let value = self.strategy.take(entry).await?;
Ok(self.compressor.decompress(value.into()).await?.into_owned())
}
pub async fn delete(&mut self, key: K) -> Result<()> {
let entry = self.data.remove(&key).ok_or(crate::Error::KeyNotFound)?;
self.strategy.delete(entry).await
}
pub fn exists(&self, key: K) -> bool {
self.data.contains_key(&key)
}
pub fn capacity(&self) -> Option<CacheCapacity> {
self.strategy.get_cache_capacity()
}
#[cfg(test)]
pub(crate) fn strategy(&self) -> &S {
&self.strategy
}
}
impl<K, S, C> Cache<K, S, C>
where
K: CacheKey + Eq + Hash + Send,
S: RecoverableStrategy + Send,
C: CompressionStrategy + Sync + Send,
{
pub async fn recover<F>(&mut self, key_from_str: F) -> Result<usize>
where
F: Fn(&str) -> Option<K> + Send,
{
let entries = self.strategy.recover(key_from_str).await?;
let recovered_item_count = entries.len();
for (key, entry) in entries {
self.data.insert(key, entry);
}
Ok(recovered_item_count)
}
}
impl<K, S, C> Cache<K, S, C>
where
K: CacheKey + Eq + Hash + ToOwned<Owned = K> + Sync + Send,
S: FlushableStrategy,
C: CompressionStrategy + Sync + Send,
{
pub async fn flush(&mut self) -> Result<usize> {
let mut flushed_item_count = 0;
let mut keys_to_remove = Vec::<K>::new();
let mut entries_to_insert = Vec::new();
for (key, entry) in self.data.iter() {
let Some(new_entry) = self.strategy.flush(key, entry).await? else {
continue;
};
keys_to_remove.push(key.to_owned());
entries_to_insert.push((key.to_owned(), new_entry));
flushed_item_count += 1;
}
for key in keys_to_remove {
let entry = self.data.remove(&key).ok_or(crate::Error::KeyNotFound)?;
self.strategy.delete(entry).await?;
}
for (key, entry) in entries_to_insert {
self.data.insert(key, entry);
}
Ok(flushed_item_count)
}
}