use std::error::Error;
use std::future::Future;
use std::marker::PhantomData;
use hydracache_core::{
CacheCodec, CacheKeyBuilder, CacheOptions, CacheStats, PostcardCodec, Result,
};
use serde::{de::DeserializeOwned, Serialize};
use crate::cache::HydraCache;
#[derive(Debug, Clone)]
pub struct TypedCache<T, C = PostcardCodec>
where
C: CacheCodec,
{
cache: HydraCache<C>,
namespace: String,
_value: PhantomData<fn() -> T>,
}
impl<T, C> TypedCache<T, C>
where
C: CacheCodec,
{
pub(crate) fn new(cache: HydraCache<C>, namespace: String) -> Self {
Self {
cache,
namespace,
_value: PhantomData,
}
}
pub fn namespace(&self) -> &str {
&self.namespace
}
pub fn key(&self, key: &str) -> String {
format!("{}:{key}", self.namespace)
}
pub fn key_from(&self, builder: CacheKeyBuilder) -> String {
let key = builder.build_string();
if key.is_empty() {
self.namespace.clone()
} else {
format!("{}:{key}", self.namespace)
}
}
}
impl<T, C> TypedCache<T, C>
where
T: Serialize + DeserializeOwned,
C: CacheCodec,
{
pub async fn get(&self, key: &str) -> Result<Option<T>> {
self.cache.get(&self.key(key)).await
}
pub async fn put(&self, key: &str, value: T, options: CacheOptions) -> Result<()> {
self.cache.put(&self.key(key), value, options).await
}
pub async fn get_or_load<E, F, Fut>(
&self,
key: &str,
options: CacheOptions,
loader: F,
) -> Result<T>
where
E: Error + Send + Sync + 'static,
F: FnOnce() -> Fut + Send + 'static,
Fut: Future<Output = std::result::Result<T, E>> + Send + 'static,
{
self.cache
.get_or_load(&self.key(key), options, loader)
.await
}
pub async fn get_or_insert_with<F, Fut>(
&self,
key: &str,
options: CacheOptions,
loader: F,
) -> Result<T>
where
F: FnOnce() -> Fut + Send + 'static,
Fut: Future<Output = T> + Send + 'static,
{
self.cache
.get_or_insert_with(&self.key(key), options, loader)
.await
}
pub async fn try_get_or_insert_with<E, F, Fut>(
&self,
key: &str,
options: CacheOptions,
loader: F,
) -> Result<T>
where
E: Error + Send + Sync + 'static,
F: FnOnce() -> Fut + Send + 'static,
Fut: Future<Output = std::result::Result<T, E>> + Send + 'static,
{
self.cache
.try_get_or_insert_with(&self.key(key), options, loader)
.await
}
pub async fn remove(&self, key: &str) -> Result<bool> {
self.cache.remove(&self.key(key)).await
}
pub async fn invalidate_key(&self, key: &str) -> Result<bool> {
self.remove(key).await
}
pub async fn contains_key(&self, key: &str) -> bool {
self.cache.contains_key(&self.key(key)).await
}
pub async fn invalidate_tag(&self, tag: &str) -> Result<u64> {
self.cache.invalidate_tag(tag).await
}
pub async fn flush(&self) -> Result<()> {
self.cache.flush().await
}
pub fn stats(&self) -> CacheStats {
self.cache.stats()
}
}