use alloc::boxed::Box;
use core::{
any::{Any, TypeId},
convert::TryFrom,
marker::PhantomData,
ops::{Deref, DerefMut},
};
#[derive(Debug)]
pub struct Registry {
data: RegistryData,
}
impl Registry {
#[inline]
pub fn new() -> Self {
Self {
data: Default::default(),
}
}
#[inline]
pub fn data(&self) -> &RegistryData {
&self.data
}
#[inline]
pub fn data_mut(&mut self) -> &mut RegistryData {
&mut self.data
}
pub fn register<R: Any>(&mut self, key: RegistryKey, value: R) {
if !self.contains_key(&key) {
self.data.insert(key, RefCell::new(Box::new(value)));
}
}
#[inline]
pub fn contains_key(&self, key: &RegistryKey) -> bool {
self.data.contains_key(key)
}
#[inline]
pub fn get<'a, R: Any>(&'a self, key: &RegistryKey) -> Option<Ref<'a, R>> {
self.data.get(key).map(RefCell::borrow).try_into().ok()
}
#[inline]
pub fn get_mut<'a, R: Any>(&'a self, key: &RegistryKey) -> Option<RefMut<'a, R>> {
self.data.get(key).map(RefCell::borrow_mut).try_into().ok()
}
}
impl Default for Registry {
#[inline]
fn default() -> Self {
Self::new()
}
}
pub type RegistryData = BackingMap<RegistryKey, RefCell<Box<dyn Any>>>;
type RefCell<T> = core::cell::RefCell<T>;
#[cfg(feature = "std")]
type BackingMap<K, V> = std::collections::HashMap<K, V>;
#[cfg(not(feature = "std"))]
type BackingMap<K, V> = alloc::collections::BTreeMap<K, V>;
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum RegistryKey {
Type(TypeId),
Id(u32),
}
impl RegistryKey {
#[inline]
pub fn from_type<T: Any>() -> Self {
RegistryKey::Type(TypeId::of::<T>())
}
#[inline]
pub fn from_id(id: u32) -> Self {
RegistryKey::Id(id)
}
}
pub struct Ref<'a, T: Any> {
guard: core::cell::Ref<'a, Box<dyn Any>>,
phantom: PhantomData<&'a T>,
}
impl<'a, T: Any> TryFrom<Option<core::cell::Ref<'a, Box<dyn Any>>>> for Ref<'a, T> {
type Error = &'static str;
#[inline]
fn try_from(guard: Option<core::cell::Ref<'a, Box<dyn Any>>>) -> Result<Self, Self::Error> {
guard
.and_then(|guard| {
if guard.deref().downcast_ref::<T>().is_some() {
Some(Self {
guard,
phantom: PhantomData,
})
} else {
None
}
})
.ok_or("resource not registered")
}
}
impl<T: Any> Deref for Ref<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
self.guard
.deref()
.downcast_ref()
.expect("resource not registered")
}
}
pub struct RefMut<'a, T> {
guard: core::cell::RefMut<'a, Box<dyn Any>>,
phantom: PhantomData<&'a mut T>,
}
impl<'a, T: Any> TryFrom<Option<core::cell::RefMut<'a, Box<dyn Any>>>> for RefMut<'a, T> {
type Error = &'static str;
#[inline]
fn try_from(guard: Option<core::cell::RefMut<'a, Box<dyn Any>>>) -> Result<Self, Self::Error> {
guard
.and_then(|guard| {
if guard.deref().downcast_ref::<T>().is_some() {
Some(Self {
guard,
phantom: PhantomData,
})
} else {
None
}
})
.ok_or("resource not registered")
}
}
impl<T: Any> Deref for RefMut<'_, T> {
type Target = T;
#[inline]
fn deref(&self) -> &Self::Target {
self.guard
.deref()
.downcast_ref()
.expect("resource not registered")
}
}
impl<T: Any> DerefMut for RefMut<'_, T> {
#[inline]
fn deref_mut(&mut self) -> &mut Self::Target {
self.guard
.deref_mut()
.downcast_mut()
.expect("resource not registered")
}
}