lyra-engine/lyra-ecs/src/resource.rs

65 lines
2.1 KiB
Rust
Raw Normal View History

2023-11-29 04:25:47 +00:00
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 Resource: Send + Sync + 'static {}
impl<T: Send + Sync + 'static> Resource 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<NonNull<u8>>,
type_id: TypeId,
layout: Layout,
}
impl ResourceData {
pub fn new<T: 'static>(mut data: T) -> Self {
let layout = Layout::new::<T>();
let ptr = unsafe {
if let Some(ptr) = NonNull::new(alloc::alloc(layout)) {
let data = (&mut data as *mut T) as *mut u8; // thx i hate it
std::ptr::copy_nonoverlapping(data, ptr.as_ptr(), 1);
ptr
} else {
alloc::handle_alloc_error(layout)
}
};
Self {
data: RefCell::new(ptr),
type_id: TypeId::of::<T>(),
layout,
}
}
/// Returns a boolean indicating whether or not T is of the same type that is inside this Resource
pub fn is<T: 'static>(&self) -> bool {
self.type_id == TypeId::of::<T>()
}
/// 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::<T>() == 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.
///
/// # Safety
///
/// If the type of T is not the same as the expected type, it will panic.
pub fn get_mut<'a, T: 'static>(&'a self) -> RefMut<'a, T> {
assert!(TypeId::of::<T>() == self.type_id);
let data = self.data.borrow_mut();
RefMut::map(data, |ptr| unsafe { &mut *ptr.cast().as_ptr() })
}
}