use std::{any::TypeId, alloc::Layout, cell::{RefCell, Ref, RefMut}, ptr::NonNull, alloc}; /// Shorthand for `Send + Sync + 'static`, so it never needs to be implemented manually. pub trait ResourceObject: Send + Sync + 'static {} impl ResourceObject for T {} /// A type erased storage for a Resource. /// /// A pointer instead of a generic is used since Rust may have no idea of the type of the data. pub struct ResourceData { data: RefCell>, type_id: TypeId, layout: Layout, } impl ResourceData { pub fn new(data: T) -> Self { let layout = Layout::new::(); let data = NonNull::from(&data).cast(); let ptr = unsafe { if let Some(ptr) = NonNull::new(alloc::alloc(layout)) { std::ptr::copy_nonoverlapping(data.as_ptr(), ptr.as_ptr(), layout.size()); ptr } else { alloc::handle_alloc_error(layout) } }; Self { data: RefCell::new(ptr), type_id: TypeId::of::(), layout, } } /// Returns a boolean indicating whether or not T is of the same type that is inside this Resource pub fn is(&self) -> bool { self.type_id == TypeId::of::() } /// Borrow the data inside of the resource. /// /// # Safety /// /// If the type of T is not the same as the expected type, it will panic. pub fn get<'a, T: 'static>(&'a self) -> Ref<'a, T> { assert!(TypeId::of::() == self.type_id); let data = self.data.borrow(); Ref::map(data, |ptr| unsafe { &*ptr.cast().as_ptr() }) } /// Mutably borrow the data inside of the resource. /// /// # Panics /// /// * If the type of T is not the same as the expected type, it will panic. /// * If the data is already borrowed mutably, this will panic. pub fn get_mut<'a, T: 'static>(&'a self) -> RefMut<'a, T> { assert!(TypeId::of::() == self.type_id); let data = self.data.borrow_mut(); RefMut::map(data, |ptr| unsafe { &mut *ptr.cast().as_ptr() }) } /// Borrow the data inside of the resource. /// /// # Safety /// /// If the type of T is not the same as the expected type, it will panic. pub fn try_get<'a, T: 'static>(&'a self) -> Option> { assert!(TypeId::of::() == self.type_id); self.data.try_borrow() .map(|r| Ref::map(r, |ptr| unsafe { &*ptr.cast().as_ptr() })) .ok() } /// Mutably borrow the data inside of the resource. /// /// # Panics /// /// * If the type of T is not the same as the expected type, it will panic. /// * If the data is already borrowed mutably, this will panic. pub fn try_get_mut<'a, T: 'static>(&'a self) -> Option> { assert!(TypeId::of::() == self.type_id); self.data.try_borrow_mut() .map(|r| RefMut::map(r, |ptr| unsafe { &mut *ptr.cast().as_ptr() })) .ok() } }