#![allow(dead_code)]
#[macro_export]
macro_rules! define_registry {
($name:ident) => {
pub mod $name {
use std::sync::{Arc, LazyLock, Mutex};
use std::collections::HashMap;
use std::any::{TypeId, Any};
static STORAGE: LazyLock<Mutex<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
type TraceCallback = LazyLock<Mutex<Option<Arc<dyn Fn(&$crate::RegistryEvent) + Send + Sync>>>>;
static TRACE: TraceCallback = LazyLock::new(|| Mutex::new(None));
struct Api;
impl $crate::RegistryApi for Api {
fn storage() -> &'static LazyLock<Mutex<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>> {
&STORAGE
}
fn trace() -> &'static TraceCallback {
&TRACE
}
}
const API: Api = Api;
pub fn register<T: Send + Sync + 'static>(value: T) {
use $crate::RegistryApi;
API.register(value)
}
pub fn register_arc<T: Send + Sync + 'static>(value: Arc<T>) {
use $crate::RegistryApi;
API.register_arc(value)
}
pub fn get<T: Send + Sync + 'static>() -> Result<Arc<T>, $crate::RegistryError> {
use $crate::RegistryApi;
API.get()
}
pub fn get_cloned<T: Send + Sync + Clone + 'static>() -> Result<T, $crate::RegistryError> {
use $crate::RegistryApi;
API.get_cloned()
}
pub fn try_get<T: Send + Sync + 'static>() -> Option<Arc<T>> {
use $crate::RegistryApi;
API.try_get()
}
pub fn contains<T: Send + Sync + 'static>() -> Result<bool, $crate::RegistryError> {
use $crate::RegistryApi;
API.contains::<T>()
}
pub fn set_trace_callback(callback: impl Fn(&$crate::RegistryEvent) + Send + Sync + 'static) {
use $crate::RegistryApi;
API.set_trace_callback(callback)
}
pub fn clear_trace_callback() {
use $crate::RegistryApi;
API.clear_trace_callback()
}
#[doc(hidden)]
pub fn clear() {
use $crate::RegistryApi;
API.clear()
}
}
};
}
#[cfg(test)]
mod tests {
use std::sync::Arc;
#[test]
fn test_define_registry_macro() {
define_registry!(test_reg);
test_reg::register(100i32);
let value: Arc<i32> = test_reg::get().unwrap();
assert_eq!(*value, 100);
assert!(test_reg::contains::<i32>().unwrap());
assert!(!test_reg::contains::<f64>().unwrap());
}
#[test]
fn test_multiple_registries() {
define_registry!(reg_a);
define_registry!(reg_b);
reg_a::register(1i32);
reg_b::register(2i32);
let a_val: Arc<i32> = reg_a::get().unwrap();
let b_val: Arc<i32> = reg_b::get().unwrap();
assert_eq!(*a_val, 1);
assert_eq!(*b_val, 2);
}
#[test]
fn test_tracing() {
define_registry!(trace_test);
use std::sync::Mutex;
let events = Arc::new(Mutex::new(Vec::new()));
let events_clone = events.clone();
trace_test::set_trace_callback(move |event| {
events_clone.lock().unwrap().push(format!("{}", event));
});
trace_test::register(42i32);
let _: Arc<i32> = trace_test::get().unwrap();
let _ = trace_test::contains::<i32>();
let recorded = events.lock().unwrap();
assert_eq!(recorded.len(), 4);
assert!(recorded[0].contains("register"));
assert!(recorded[1].contains("register_completed"));
assert!(recorded[2].contains("get"));
assert!(recorded[3].contains("contains"));
}
#[test]
fn test_additional_functions() {
define_registry!(extra_test);
let val = Arc::new(99i32);
extra_test::register_arc(val);
let cloned: i32 = extra_test::get_cloned().unwrap();
assert_eq!(cloned, 99);
extra_test::set_trace_callback(|_| {});
extra_test::clear_trace_callback(); }
#[test]
fn test_try_get() {
define_registry!(try_get_test);
assert!(try_get_test::try_get::<i32>().is_none());
try_get_test::register(77i32);
let val = try_get_test::try_get::<i32>().expect("should be Some after register");
assert_eq!(*val, 77);
}
}