use once_cell::sync::Lazy;
use parking_lot::RwLock;
use std::collections::HashMap;
use crate::CacheStats;
static STATS_REGISTRY: Lazy<RwLock<HashMap<String, &'static Lazy<CacheStats>>>> =
Lazy::new(|| RwLock::new(HashMap::new()));
pub fn register(name: &str, stats: &'static Lazy<CacheStats>) {
let mut registry = STATS_REGISTRY.write();
registry.insert(name.to_string(), stats);
}
pub fn get(name: &str) -> Option<CacheStats> {
let registry = STATS_REGISTRY.read();
registry.get(name).map(|stats| (**stats).clone())
}
pub fn get_ref(name: &str) -> Option<&'static CacheStats> {
let registry = STATS_REGISTRY.read();
registry.get(name).map(|stats| &***stats)
}
pub fn list() -> Vec<String> {
let registry = STATS_REGISTRY.read();
registry.keys().cloned().collect()
}
pub fn clear() {
let mut registry = STATS_REGISTRY.write();
registry.clear();
}
pub fn reset(name: &str) -> bool {
let registry = STATS_REGISTRY.read();
if let Some(stats) = registry.get(name) {
stats.reset();
true
} else {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use serial_test::serial;
#[test]
#[serial]
fn test_register_and_get() {
clear();
static TEST_STATS: Lazy<CacheStats> = Lazy::new(|| CacheStats::new());
register("test_fn", &TEST_STATS);
let stats = get("test_fn");
assert!(stats.is_some());
let stats = stats.unwrap();
assert_eq!(stats.hits(), 0);
assert_eq!(stats.misses(), 0);
}
#[test]
#[serial]
fn test_get_ref() {
clear();
static TEST_STATS2: Lazy<CacheStats> = Lazy::new(|| CacheStats::new());
register("test_fn2", &TEST_STATS2);
TEST_STATS2.record_hit();
TEST_STATS2.record_miss();
let stats = get_ref("test_fn2");
assert!(stats.is_some());
let stats = stats.unwrap();
assert_eq!(stats.hits(), 1);
assert_eq!(stats.misses(), 1);
}
#[test]
#[serial]
fn test_list() {
clear();
static TEST_STATS3: Lazy<CacheStats> = Lazy::new(|| CacheStats::new());
static TEST_STATS4: Lazy<CacheStats> = Lazy::new(|| CacheStats::new());
register("fn1", &TEST_STATS3);
register("fn2", &TEST_STATS4);
let names = list();
assert!(names.contains(&"fn1".to_string()));
assert!(names.contains(&"fn2".to_string()));
}
#[test]
#[serial]
fn test_reset() {
clear();
static TEST_STATS5: Lazy<CacheStats> = Lazy::new(|| CacheStats::new());
register("test_fn5", &TEST_STATS5);
TEST_STATS5.record_hit();
TEST_STATS5.record_hit();
assert_eq!(TEST_STATS5.hits(), 2);
assert!(reset("test_fn5"));
assert_eq!(TEST_STATS5.hits(), 0);
assert!(!reset("nonexistent"));
}
#[test]
#[serial]
fn test_clear() {
clear();
static TEST_STATS6: Lazy<CacheStats> = Lazy::new(|| CacheStats::new());
register("test_fn6", &TEST_STATS6);
assert!(!list().is_empty());
clear();
assert!(list().is_empty());
}
}