use alloc::boxed::Box;
use core::{
any::{type_name, Any, TypeId},
borrow::{Borrow, BorrowMut},
cmp::Ordering,
fmt::{self, Debug, Display},
hash::{Hash, Hasher},
ops::{Deref, DerefMut},
};
use atomicell::{AtomicCell, Ref, RefMut};
use hashbrown::HashMap;
use crate::type_id;
#[repr(transparent)]
#[derive(Clone)]
pub struct Res<'a, T: ?Sized> {
inner: Ref<'a, T>,
}
impl<'a, T> Deref for Res<'a, T>
where
T: ?Sized,
{
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.inner.deref()
}
}
impl<T> fmt::Debug for Res<'_, T>
where
T: Debug + ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<T as Debug>::fmt(self, f)
}
}
impl<T> fmt::Display for Res<'_, T>
where
T: Display + ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<T as Display>::fmt(self, f)
}
}
impl<'a, T, U> PartialEq<U> for Res<'a, T>
where
T: PartialEq<U> + ?Sized,
{
#[inline]
fn eq(&self, other: &U) -> bool {
<T as PartialEq<U>>::eq(self, other)
}
}
impl<'a, T, U> PartialOrd<U> for Res<'a, T>
where
T: PartialOrd<U> + ?Sized,
{
#[inline]
fn partial_cmp(&self, other: &U) -> Option<Ordering> {
<T as PartialOrd<U>>::partial_cmp(self, other)
}
}
impl<'a, T> Hash for Res<'a, T>
where
T: Hash + ?Sized,
{
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
<T as Hash>::hash(self, state)
}
}
impl<'a, T> Borrow<T> for Res<'a, T>
where
T: ?Sized,
{
#[inline]
fn borrow(&self) -> &T {
self
}
}
impl<'a, T, U> AsRef<U> for Res<'a, T>
where
T: AsRef<U> + ?Sized,
{
#[inline]
fn as_ref(&self) -> &U {
<T as AsRef<U>>::as_ref(self)
}
}
impl<'a, T> Res<'a, T>
where
T: ?Sized,
{
#[inline]
pub fn leak(r: Res<'a, T>) -> &'a T {
Ref::leak(r.inner)
}
}
pub struct ResMut<'a, T: ?Sized> {
inner: RefMut<'a, T>,
}
impl<'a, T> Deref for ResMut<'a, T>
where
T: ?Sized,
{
type Target = T;
#[inline]
fn deref(&self) -> &T {
self.inner.deref()
}
}
impl<'a, T> DerefMut for ResMut<'a, T>
where
T: ?Sized,
{
#[inline]
fn deref_mut(&mut self) -> &mut T {
self.inner.deref_mut()
}
}
impl<T> fmt::Debug for ResMut<'_, T>
where
T: Debug + ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<T as Debug>::fmt(self, f)
}
}
impl<T> fmt::Display for ResMut<'_, T>
where
T: Display + ?Sized,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
<T as Display>::fmt(self, f)
}
}
impl<'a, T, U> PartialEq<U> for ResMut<'a, T>
where
T: PartialEq<U> + ?Sized,
{
#[inline]
fn eq(&self, other: &U) -> bool {
<T as PartialEq<U>>::eq(self, other)
}
}
impl<'a, T, U> PartialOrd<U> for ResMut<'a, T>
where
T: PartialOrd<U> + ?Sized,
{
#[inline]
fn partial_cmp(&self, other: &U) -> Option<Ordering> {
<T as PartialOrd<U>>::partial_cmp(self, other)
}
}
impl<'a, T> Hash for ResMut<'a, T>
where
T: Hash + ?Sized,
{
#[inline]
fn hash<H>(&self, state: &mut H)
where
H: Hasher,
{
<T as Hash>::hash(self, state)
}
}
impl<'a, T> Borrow<T> for ResMut<'a, T>
where
T: ?Sized,
{
#[inline]
fn borrow(&self) -> &T {
self
}
}
impl<'a, T> BorrowMut<T> for ResMut<'a, T>
where
T: ?Sized,
{
#[inline]
fn borrow_mut(&mut self) -> &mut T {
self
}
}
impl<'a, T, U> AsRef<U> for ResMut<'a, T>
where
T: AsRef<U> + ?Sized,
{
#[inline]
fn as_ref(&self) -> &U {
<T as AsRef<U>>::as_ref(self)
}
}
impl<'a, T, U> AsMut<U> for ResMut<'a, T>
where
T: AsMut<U> + ?Sized,
{
#[inline]
fn as_mut(&mut self) -> &mut U {
<T as AsMut<U>>::as_mut(self)
}
}
impl<'a, T> ResMut<'a, T>
where
T: ?Sized,
{
#[inline]
pub fn leak(r: ResMut<'a, T>) -> &'a mut T {
RefMut::leak(r.inner)
}
}
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(crate) struct Resources {
resources: HashMap<TypeId, Resource>,
}
impl Debug for Resources {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self.resources)
}
}
impl Resources {
#[inline]
pub fn new() -> Self {
Resources {
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]
#[track_caller]
pub fn get<T: Sync + 'static>(&self) -> Option<Res<'_, T>> {
unsafe {
self.get_local()
}
}
#[inline]
#[track_caller]
pub fn get_mut<T: Send + 'static>(&self) -> Option<ResMut<'_, T>> {
unsafe {
self.get_local_mut()
}
}
#[inline]
#[track_caller]
pub unsafe fn get_local<T: 'static>(&self) -> Option<Res<'_, 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(Res { inner: r })
}
#[inline]
#[track_caller]
pub unsafe fn get_local_mut<T: 'static>(&self) -> Option<ResMut<'_, 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(ResMut { inner: r })
}
pub fn undo_leaks(&mut self) {
for (_, r) in self.resources.iter_mut() {
r.data.undo_leak();
}
}
#[inline]
pub fn resource_types(&self) -> impl Iterator<Item = TypeId> + '_ {
self.resources.keys().copied()
}
}