use alloc::boxed::Box;
use core::{
any::{type_name, Any, TypeId},
fmt::{self, Debug},
};
use atomicell::{AtomicCell, Ref, RefMut};
use hashbrown::HashMap;
use crate::type_id;
struct Resource {
data: Box<AtomicCell<dyn Any>>,
name: &'static str,
}
impl Debug for Resource {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(self.name)
}
}
unsafe impl Sync for Resource {}
pub struct Res {
resources: HashMap<TypeId, Resource>,
}
impl Debug for Res {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.resources)
}
}
impl Res {
#[inline(always)]
pub fn new() -> Self {
Res {
resources: HashMap::new(),
}
}
pub fn insert<T: 'static>(&mut self, resource: T) {
let id = type_id::<T>();
self.resources.insert(
id,
Resource {
data: Box::new(AtomicCell::new(resource)),
name: type_name::<T>(),
},
);
}
pub fn with<T: 'static>(&mut self, f: impl FnOnce() -> T) -> &mut T {
let id = type_id::<T>();
self.resources
.entry(id)
.or_insert_with(|| Resource {
data: Box::new(AtomicCell::new(f())),
name: type_name::<T>(),
})
.data
.get_mut()
.downcast_mut()
.unwrap()
}
pub fn remove<T: 'static>(&mut self) -> Option<T> {
let mut resource = self.resources.remove(&type_id::<T>())?;
let data = AtomicCell::into_inner(*unsafe {
assert!(resource.data.get_mut().is::<T>());
Box::from_raw(Box::into_raw(resource.data) as *mut AtomicCell<T>)
});
Some(data)
}
#[inline(always)]
#[track_caller]
pub fn get<T: Sync + 'static>(&self) -> Option<Ref<T>> {
unsafe {
self.get_local()
}
}
#[inline(always)]
#[track_caller]
pub fn get_mut<T: Send + 'static>(&self) -> Option<RefMut<T>> {
unsafe {
self.get_local_mut()
}
}
#[inline(always)]
#[track_caller]
pub unsafe fn get_local<T: 'static>(&self) -> Option<Ref<T>> {
let id = type_id::<T>();
let resource = self.resources.get(&id)?;
let r = {
&resource.data
}
.try_borrow();
let Some(r) = r else {
panic!(
"Attempt to borrow {} when it is already borrowed mutably",
type_name::<T>()
);
};
let r = Ref::map(r, |r| r.downcast_ref::<T>().unwrap());
Some(r)
}
#[inline(always)]
#[track_caller]
pub unsafe fn get_local_mut<T: 'static>(&self) -> Option<RefMut<T>> {
let id = type_id::<T>();
let resource = self.resources.get(&id)?;
let r = {
&resource.data
}
.try_borrow_mut();
let Some(r) = r else {
panic!(
"Attempt to borrow {} mutably when it is already borrowed",
type_name::<T>()
);
};
let r = RefMut::map(r, |r| r.downcast_mut::<T>().unwrap());
Some(r)
}
pub fn undo_leak(&mut self) {
for (_, r) in self.resources.iter_mut() {
r.data.undo_leak();
}
}
#[inline(always)]
pub fn resource_types(&self) -> impl Iterator<Item = TypeId> + '_ {
self.resources.keys().copied()
}
}