use std::sync::Arc;
pub trait CacheMetrics: Send + Sync {
fn hits(&self) -> u64;
fn misses(&self) -> u64;
fn hit_rate(&self) -> f64 {
let total = self.hits() + self.misses();
if total == 0 {
0.0
} else {
(self.hits() as f64 / total as f64) * 100.0
}
}
}
pub trait AuthzCache<V: Clone + Send + Sync>: Send + Sync {
fn get(&self, key: &str) -> Option<V>;
fn insert(&self, key: &str, value: V);
fn invalidate(&self, key: &str);
fn invalidate_all(&self);
fn metrics(&self) -> Box<dyn CacheMetrics>;
}
pub struct NoopCache;
struct NoopMetrics;
impl CacheMetrics for NoopMetrics {
fn hits(&self) -> u64 {
0
}
fn misses(&self) -> u64 {
0
}
}
impl<V: Clone + Send + Sync> AuthzCache<V> for NoopCache {
fn get(&self, _key: &str) -> Option<V> {
None
}
fn insert(&self, _key: &str, _value: V) {}
fn invalidate(&self, _key: &str) {}
fn invalidate_all(&self) {}
fn metrics(&self) -> Box<dyn CacheMetrics> {
Box::new(NoopMetrics)
}
}
pub fn noop_cache<V: Clone + Send + Sync + 'static>() -> Arc<dyn AuthzCache<V>> {
Arc::new(NoopCache)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn noop_cache_always_misses() {
let cache: Arc<dyn AuthzCache<String>> = noop_cache();
cache.insert("key", "value".to_string());
assert_eq!(cache.get("key"), None);
}
#[test]
fn noop_cache_invalidate_is_safe() {
let cache: Arc<dyn AuthzCache<i32>> = noop_cache();
cache.invalidate_all(); }
}