use std::{
collections::HashMap,
sync::{Arc, RwLock},
};
use crate::fused_rw::FusedRw;
type Item<T> = Arc<FusedRw<Option<Arc<T>>>>;
#[derive(Clone)]
pub struct LocalCache<T> {
cache: Arc<RwLock<HashMap<String, Item<T>>>>,
}
impl<T> LocalCache<T> {
pub fn new() -> Self {
Self {
cache: Arc::new(RwLock::new(HashMap::new())),
}
}
pub fn is_cached(&self, path: impl AsRef<str>) -> bool {
self.cache
.read()
.expect("Cache cannot poison")
.get(path.as_ref())
.is_some_and(|e| e.read().is_some())
}
pub async fn is_cached_async(&self, path: impl AsRef<str>) -> bool {
let Some(entry) = self
.cache
.read()
.expect("Cache cannot poison")
.get(path.as_ref())
.cloned()
else {
return false;
};
entry.read_async().await.is_some()
}
pub fn get(&self, path: impl AsRef<str>) -> Option<Arc<T>> {
let entry = {
let guard = self.cache.read().expect("Cache cannot poison");
let entry = guard.get(path.as_ref())?.clone();
drop(guard);
entry
};
entry.read().clone()
}
pub async fn get_async(&self, path: impl AsRef<str>) -> Option<Arc<T>> {
let entry = {
let guard = self.cache.read().expect("Cache cannot poison");
let entry = guard.get(path.as_ref())?.clone();
drop(guard);
entry
};
entry.read_async().await.clone()
}
pub fn get_or(&self, path: impl AsRef<str>, default: Arc<T>) -> Arc<T> {
if let Some(good) = self.get(path.as_ref()) {
return good;
}
let entry = {
let mut guard = self.cache.write().expect("Cache cannot poison");
guard
.entry(path.as_ref().to_string())
.or_insert_with(|| Arc::new(FusedRw::new(None)))
.clone()
};
if let Some(good) = entry.read().as_ref() {
good.clone()
} else {
assert!(
entry.write().replace(default.clone()).is_none(),
"Cached value should be None"
);
default
}
}
pub async fn get_or_async(&self, path: impl AsRef<str>, default: Arc<T>) -> Arc<T> {
if let Some(good) = self.get_async(path.as_ref()).await {
return good;
}
let entry = {
let mut guard = self.cache.write().expect("Cache cannot poison");
guard
.entry(path.as_ref().to_string())
.or_insert_with(|| Arc::new(FusedRw::new(None)))
.clone()
};
if let Some(good) = entry.read_async().await.as_ref() {
good.clone()
} else {
assert!(
entry.write_async().await.replace(default.clone()).is_none(),
"Cached value should be None"
);
default
}
}
pub fn get_or_else<F: FnOnce() -> Arc<T>>(&self, path: impl AsRef<str>, f: F) -> Arc<T> {
if let Some(good) = self.get(path.as_ref()) {
return good;
}
let entry = {
let mut guard = self.cache.write().expect("Cache cannot poison");
guard
.entry(path.as_ref().to_string())
.or_insert_with(|| Arc::new(FusedRw::new(None)))
.clone()
};
if let Some(good) = entry.read().as_ref() {
good.clone()
} else {
let loaded = f();
assert!(
entry.write().replace(loaded.clone()).is_none(),
"Cached value should be None"
);
loaded
}
}
pub async fn get_or_else_async<F: AsyncFnOnce() -> Arc<T>>(
&self,
path: impl AsRef<str>,
f: F,
) -> Arc<T> {
if let Some(good) = self.get_async(path.as_ref()).await {
return good;
}
let entry = {
let mut guard = self.cache.write().expect("Cache cannot poison");
guard
.entry(path.as_ref().to_string())
.or_insert_with(|| Arc::new(FusedRw::new(None)))
.clone()
};
if let Some(good) = entry.read_async().await.as_ref() {
good.clone()
} else {
let loaded = f().await;
assert!(
entry.write_async().await.replace(loaded.clone()).is_none(),
"Cached value should be None"
);
loaded
}
}
pub fn get_or_else_try<F>(&self, path: impl AsRef<str>, f: F) -> Option<Arc<T>>
where
F: FnOnce() -> Option<Arc<T>>,
{
if let Some(good) = self.get(path.as_ref()) {
return Some(good);
}
let entry = {
let mut guard = self.cache.write().expect("Cache cannot poison");
guard
.entry(path.as_ref().to_string())
.or_insert_with(|| Arc::new(FusedRw::new(None)))
.clone()
};
if let Some(good) = entry.read().as_ref() {
Some(good.clone())
} else {
let loaded = f()?;
assert!(
entry.write().replace(loaded.clone()).is_none(),
"Cached value should be None"
);
Some(loaded)
}
}
pub async fn get_or_else_try_async<F: AsyncFnOnce() -> Option<Arc<T>>>(
&self,
path: impl AsRef<str>,
f: F,
) -> Option<Arc<T>> {
if let Some(good) = self.get_async(path.as_ref()).await {
return Some(good);
}
let entry = {
let mut guard = self.cache.write().expect("Cache cannot poison");
guard
.entry(path.as_ref().to_string())
.or_insert_with(|| Arc::new(FusedRw::new(None)))
.clone()
};
if let Some(good) = entry.read_async().await.as_ref() {
Some(good.clone())
} else {
let loaded = f().await?;
assert!(
entry.write_async().await.replace(loaded.clone()).is_none(),
"Cached value should be None"
);
Some(loaded)
}
}
pub fn uncache(&self, path: impl AsRef<str>) {
let mut guard = self.cache.write().expect("Cache cannot poison");
guard.remove(path.as_ref());
}
pub fn clear(&self) {
let mut guard = self.cache.write().expect("Cache cannot poison");
guard.clear();
}
}
impl<T> Default for LocalCache<T> {
fn default() -> Self {
Self::new()
}
}