use std::fmt;
use std::sync::Arc;
use crate::backend::interface::CacheBackend;
static CACHE_REGISTRY: once_cell::sync::OnceCell<Registry> = once_cell::sync::OnceCell::new();
struct Registry {
caches: dashmap::DashMap<String, Arc<dyn CacheBackend>>,
}
impl fmt::Debug for Registry {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Registry")
.field("cache_count", &self.caches.len())
.finish()
}
}
impl Registry {
fn new() -> Self {
Self {
caches: dashmap::DashMap::new(),
}
}
}
pub fn init(default_cache: Arc<dyn CacheBackend>) {
let registry = Registry::new();
registry.caches.insert("default".to_string(), default_cache);
CACHE_REGISTRY
.set(registry)
.expect("oxcache registry already initialized - call init() only once");
}
pub fn init_empty() {
let registry = Registry::new();
CACHE_REGISTRY
.set(registry)
.expect("oxcache registry already initialized - call init() only once");
}
pub fn is_initialized() -> bool {
CACHE_REGISTRY.get().is_some()
}
pub fn register(name: &str, cache: Arc<dyn CacheBackend>) {
let registry = CACHE_REGISTRY
.get()
.expect("oxcache registry not initialized - call init() first");
registry.caches.insert(name.to_string(), cache);
}
pub fn get(name: &str) -> Option<Arc<dyn CacheBackend>> {
CACHE_REGISTRY.get()?.caches.get(name).map(|r| r.clone())
}
pub fn remove(name: &str) -> Option<Arc<dyn CacheBackend>> {
CACHE_REGISTRY.get()?.caches.remove(name).map(|(_, v)| v)
}
pub fn clear() {
if let Some(registry) = CACHE_REGISTRY.get() {
registry.caches.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::backend::memory::MokaMemoryBackend;
fn ensure_initialized() {
if !is_initialized() {
let _ = std::panic::catch_unwind(|| init_empty());
}
}
#[test]
fn test_is_initialized_returns_bool() {
let _ = is_initialized();
}
#[test]
fn test_get_returns_none_for_nonexistent() {
assert!(get("definitely_does_not_exist_xyz_123").is_none());
}
#[test]
fn test_remove_returns_none_for_nonexistent() {
assert!(remove("definitely_does_not_exist_xyz_456").is_none());
}
#[test]
fn test_clear_does_not_panic() {
clear();
}
#[test]
fn test_register_get_remove_flow() {
ensure_initialized();
let backend: Arc<dyn CacheBackend> = Arc::new(MokaMemoryBackend::new());
register("test_flow_backend", backend);
let retrieved = get("test_flow_backend");
assert!(retrieved.is_some(), "get should return Some after register");
let removed = remove("test_flow_backend");
assert!(removed.is_some(), "remove should return Some");
assert!(
get("test_flow_backend").is_none(),
"get should return None after remove"
);
}
#[test]
fn test_register_overwrites_existing() {
ensure_initialized();
let backend1: Arc<dyn CacheBackend> = Arc::new(MokaMemoryBackend::new());
register("test_overwrite_backend", backend1);
let backend2: Arc<dyn CacheBackend> = Arc::new(MokaMemoryBackend::new());
register("test_overwrite_backend", backend2);
assert!(get("test_overwrite_backend").is_some());
remove("test_overwrite_backend");
}
#[test]
fn test_clear_removes_all_caches() {
ensure_initialized();
let backend: Arc<dyn CacheBackend> = Arc::new(MokaMemoryBackend::new());
register("test_clear_backend_1", backend.clone());
register("test_clear_backend_2", backend.clone());
assert!(get("test_clear_backend_1").is_some());
assert!(get("test_clear_backend_2").is_some());
clear();
assert!(get("test_clear_backend_1").is_none());
assert!(get("test_clear_backend_2").is_none());
}
#[test]
fn test_is_initialized_true_after_init() {
ensure_initialized();
assert!(is_initialized(), "is_initialized should return true after init");
}
}