use serial_test::serial;
use singleton_registry::{RegistryApi, RegistryEvent};
use std::any::{Any, TypeId};
use std::collections::HashMap;
use std::sync::{Arc, LazyLock, Mutex};
type TraceCallback = LazyLock<Mutex<Option<Arc<dyn Fn(&RegistryEvent) + Send + Sync>>>>;
static MY_STORAGE: LazyLock<Mutex<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
static MY_TRACE: TraceCallback = LazyLock::new(|| Mutex::new(None));
struct MyRegistry;
impl RegistryApi for MyRegistry {
fn storage() -> &'static LazyLock<Mutex<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>> {
&MY_STORAGE
}
fn trace() -> &'static TraceCallback {
&MY_TRACE
}
}
const MY_REGISTRY: MyRegistry = MyRegistry;
#[test]
#[serial]
fn test_basic_register_and_get() {
MY_REGISTRY.register(42i32);
let value: Arc<i32> = MY_REGISTRY.get().unwrap();
assert_eq!(*value, 42);
}
#[test]
#[serial]
fn test_register_multiple_types() {
MY_REGISTRY.register(100u32);
MY_REGISTRY.register("Hello".to_string());
MY_REGISTRY.register(9.221f64);
let num: Arc<u32> = MY_REGISTRY.get().unwrap();
let text: Arc<String> = MY_REGISTRY.get().unwrap();
let some_float: Arc<f64> = MY_REGISTRY.get().unwrap();
assert_eq!(*num, 100);
assert_eq!(&**text, "Hello");
assert_eq!(*some_float, 9.221);
}
#[test]
#[serial]
fn test_contains_check() {
MY_REGISTRY.register(999i64);
assert!(MY_REGISTRY.contains::<i64>().unwrap());
assert!(!MY_REGISTRY.contains::<i8>().unwrap());
}
#[test]
#[serial]
fn test_get_cloned() {
MY_REGISTRY.register("cloned".to_string());
let value: String = MY_REGISTRY.get_cloned().unwrap();
assert_eq!(value, "cloned");
}
#[test]
#[serial]
fn test_overwrite_same_type() {
MY_REGISTRY.register(10u16);
MY_REGISTRY.register(20u16);
let value: Arc<u16> = MY_REGISTRY.get().unwrap();
assert_eq!(*value, 20);
}
#[test]
#[serial]
fn test_with_tracing() {
use std::sync::atomic::{AtomicUsize, Ordering};
let event_count = Arc::new(AtomicUsize::new(0));
let event_count_clone = Arc::clone(&event_count);
MY_REGISTRY.set_trace_callback(move |_event| {
event_count_clone.fetch_add(1, Ordering::SeqCst);
});
MY_REGISTRY.register(777i32); let _: Arc<i32> = MY_REGISTRY.get().unwrap(); MY_REGISTRY.contains::<i32>().unwrap();
assert_eq!(event_count.load(Ordering::SeqCst), 4);
MY_REGISTRY.clear_trace_callback();
}
#[test]
#[serial]
fn test_register_arc_directly() {
let value = Arc::new(555u32);
MY_REGISTRY.register_arc(value);
let retrieved: Arc<u32> = MY_REGISTRY.get().unwrap();
assert_eq!(*retrieved, 555);
}
#[test]
#[serial]
fn test_custom_struct() {
#[derive(Debug, Clone)]
struct Config {
host: String,
port: u16,
}
let config = Config {
host: "localhost".to_string(),
port: 8080,
};
MY_REGISTRY.register(config);
let retrieved: Arc<Config> = MY_REGISTRY.get().unwrap();
assert_eq!(retrieved.host, "localhost");
assert_eq!(retrieved.port, 8080);
}
#[test]
#[serial]
fn test_trait_object() {
trait Service: Send + Sync {
fn name(&self) -> &str;
}
struct MyService;
impl Service for MyService {
fn name(&self) -> &str {
"MyService"
}
}
let service: Arc<dyn Service> = Arc::new(MyService);
MY_REGISTRY.register(service);
let retrieved: Arc<Arc<dyn Service>> = MY_REGISTRY.get().unwrap();
assert_eq!(retrieved.name(), "MyService");
}
static ANOTHER_STORAGE: LazyLock<Mutex<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>> =
LazyLock::new(|| Mutex::new(HashMap::new()));
static ANOTHER_TRACE: TraceCallback = LazyLock::new(|| Mutex::new(None));
struct AnotherRegistry;
impl RegistryApi for AnotherRegistry {
fn storage() -> &'static LazyLock<Mutex<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>> {
&ANOTHER_STORAGE
}
fn trace() -> &'static TraceCallback {
&ANOTHER_TRACE
}
}
const ANOTHER: AnotherRegistry = AnotherRegistry;
#[test]
#[serial]
fn test_multiple_manual_registries() {
MY_REGISTRY.register(100i32);
ANOTHER.register(200i32);
let my_val: Arc<i32> = MY_REGISTRY.get().unwrap();
let another_val: Arc<i32> = ANOTHER.get().unwrap();
assert_eq!(*my_val, 100);
assert_eq!(*another_val, 200);
}
#[cfg(test)]
mod comparison {
use super::*;
use singleton_registry::define_registry;
#[test]
fn test_macro_approach() {
define_registry!(easy);
easy::register(42i32);
let value: Arc<i32> = easy::get().unwrap();
assert_eq!(*value, 42);
}
#[test]
#[serial]
fn test_manual_approach() {
MY_REGISTRY.register(42i32);
let value: Arc<i32> = MY_REGISTRY.get().unwrap();
assert_eq!(*value, 42);
}
}
#[cfg(test)]
mod advanced {
use super::*;
struct EnhancedRegistry {
inner: MyRegistry,
}
impl EnhancedRegistry {
const fn new() -> Self {
Self { inner: MyRegistry }
}
fn register_with_log<T: Send + Sync + 'static>(&self, value: T) {
println!("Registering type: {}", std::any::type_name::<T>());
self.inner.register(value);
}
fn get_with_log<T: Send + Sync + 'static>(
&self,
) -> Result<Arc<T>, singleton_registry::RegistryError> {
println!("Getting type: {}", std::any::type_name::<T>());
self.inner.get()
}
}
#[test]
#[serial]
fn test_enhanced_registry() {
let registry = EnhancedRegistry::new();
registry.register_with_log(42i32);
let value: Arc<i32> = registry.get_with_log().unwrap();
assert_eq!(*value, 42);
}
}