mod layer;
#[cfg(feature = "cache-moka")]
mod moka_impl;
pub use layer::{CacheResponseLayer, CacheResponseService};
#[cfg(feature = "cache-moka")]
pub use moka_impl::MokaCache;
use std::any::Any;
use std::hash::{DefaultHasher, Hash, Hasher};
use std::sync::Arc;
pub trait Cache: Send + Sync + 'static {
fn get_value(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>>;
fn insert_value(&self, key: &str, value: Arc<dyn Any + Send + Sync>);
fn invalidate(&self, key: &str);
fn clear(&self);
}
pub fn get<V: Clone + Send + Sync + 'static>(cache: &dyn Cache, key: &str) -> Option<V> {
cache
.get_value(key)
.and_then(|arc| arc.downcast_ref::<V>().cloned())
}
pub fn insert<V: Clone + Send + Sync + 'static>(cache: &dyn Cache, key: &str, value: V) {
cache.insert_value(key, Arc::new(value));
}
pub trait CacheableResult {
type Ok: Clone;
type Err;
fn into_result(self) -> Result<Self::Ok, Self::Err>;
fn from_ok(ok: Self::Ok) -> Self;
}
impl<T: Clone, E> CacheableResult for Result<T, E> {
type Ok = T;
type Err = E;
fn into_result(self) -> Self {
self
}
fn from_ok(ok: T) -> Self {
Ok(ok)
}
}
#[must_use]
pub fn make_cache_key<K: Hash>(fn_name: &str, args: &K) -> String {
let mut hasher = DefaultHasher::new();
args.hash(&mut hasher);
format!("{}:{:x}", fn_name, hasher.finish())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cache_key_deterministic() {
let k1 = make_cache_key("get_user", &(42_i64,));
let k2 = make_cache_key("get_user", &(42_i64,));
assert_eq!(k1, k2);
}
#[test]
fn cache_key_differs_by_fn_name() {
let k1 = make_cache_key("get_user", &(42_i64,));
let k2 = make_cache_key("find_user", &(42_i64,));
assert_ne!(k1, k2);
}
#[test]
fn cache_key_differs_by_args() {
let k1 = make_cache_key("get_user", &(1_i64,));
let k2 = make_cache_key("get_user", &(2_i64,));
assert_ne!(k1, k2);
}
#[test]
fn cache_key_no_args() {
let k = make_cache_key("get_config", &());
assert!(k.starts_with("get_config:"));
}
}