use std::{
any::{Any, TypeId},
collections::HashMap,
sync::Mutex,
};
use lazy_static::lazy_static;
lazy_static! {
static ref REGISTRY: Mutex<HashMap<TypeId, Box<dyn Any + Send>>> = Mutex::new(HashMap::new());
}
pub struct Registry<T> {
_use: Option<T>,
}
impl<T: Send + 'static> Registry<T> {
pub fn register(key: &str, value: T) {
let mut registry = REGISTRY.lock().unwrap();
if !registry.contains_key(&TypeId::of::<T>()) {
let map: HashMap<String, T> = HashMap::new();
registry.insert(TypeId::of::<T>(), Box::new(map));
}
let map = registry
.get_mut(&TypeId::of::<T>())
.unwrap()
.downcast_mut::<HashMap<String, T>>()
.unwrap();
map.insert(key.to_string(), value);
}
pub fn apply<R, F: FnOnce(&mut T) -> R>(key: &str, f: F) -> Option<R> {
let mut registry = REGISTRY.lock().unwrap();
let map = registry
.get_mut(&TypeId::of::<T>())?
.downcast_mut::<HashMap<String, T>>()?;
let value = map.get_mut(key)?;
Some(f(value))
}
pub fn remove(key: &str) -> Option<T> {
let mut registry = REGISTRY.lock().unwrap();
let map = registry
.get_mut(&TypeId::of::<T>())?
.downcast_mut::<HashMap<String, T>>()?;
map.remove(key)
}
pub fn with<R, F: FnOnce(&mut T) -> R>(key: &str, f: F) -> Option<R> {
let mut value = Self::remove(key)?;
let result = f(&mut value);
Self::register(key, value);
Some(result)
}
}
#[allow(unused_imports)]
#[macro_use]
extern crate constcat;
#[macro_export]
macro_rules! id {
($($x:ident).+) => {
concat!($('.', stringify!($x)),+)
};
(@ $root:ident . $($x:ident).+) => {
{
const ROOT: &str = $root;
const PATH: &str = concat!($('.', stringify!($x)),+);
constcat::concat!(ROOT, PATH)
}
}
}