pub mod drivers;
use std::future::Future;
use std::time::Duration;
use self::drivers::CacheDriver;
use crate::Result as LocoResult;
#[derive(thiserror::Error, Debug)]
#[allow(clippy::module_name_repetitions)]
pub enum CacheError {
#[error(transparent)]
Any(#[from] Box<dyn std::error::Error + Send + Sync>),
}
pub type CacheResult<T> = std::result::Result<T, CacheError>;
pub struct Cache {
pub driver: Box<dyn CacheDriver>,
}
impl Cache {
#[must_use]
pub fn new(driver: Box<dyn CacheDriver>) -> Self {
Self { driver }
}
pub async fn contains_key(&self, key: &str) -> CacheResult<bool> {
self.driver.contains_key(key).await
}
pub async fn get(&self, key: &str) -> CacheResult<Option<String>> {
self.driver.get(key).await
}
pub async fn insert(&self, key: &str, value: &str) -> CacheResult<()> {
self.driver.insert(key, value).await
}
pub async fn insert_with_expiry(
&self,
key: &str,
value: &str,
duration: Duration,
) -> CacheResult<()> {
self.driver.insert_with_expiry(key, value, duration).await
}
pub async fn get_or_insert<F>(&self, key: &str, f: F) -> LocoResult<String>
where
F: Future<Output = LocoResult<String>> + Send,
{
if let Some(value) = self.driver.get(key).await? {
Ok(value)
} else {
let value = f.await?;
self.driver.insert(key, &value).await?;
Ok(value)
}
}
pub async fn get_or_insert_with_expiry<F>(
&self,
key: &str,
duration: Duration,
f: F,
) -> LocoResult<String>
where
F: Future<Output = LocoResult<String>> + Send,
{
if let Some(value) = self.driver.get(key).await? {
Ok(value)
} else {
let value = f.await?;
self.driver
.insert_with_expiry(key, &value, duration)
.await?;
Ok(value)
}
}
pub async fn remove(&self, key: &str) -> CacheResult<()> {
self.driver.remove(key).await
}
pub async fn clear(&self) -> CacheResult<()> {
self.driver.clear().await
}
}
#[cfg(test)]
mod tests {
use crate::tests_cfg;
#[tokio::test]
async fn can_get_or_insert() {
let app_ctx = tests_cfg::app::get_app_context().await;
let get_key = "loco";
assert_eq!(app_ctx.cache.get(get_key).await.unwrap(), None);
let result = app_ctx
.cache
.get_or_insert(get_key, async { Ok("loco-cache-value".to_string()) })
.await
.unwrap();
assert_eq!(result, "loco-cache-value".to_string());
assert_eq!(
app_ctx.cache.get(get_key).await.unwrap(),
Some("loco-cache-value".to_string())
);
}
}