use std::{any::{Any, TypeId}, collections::HashMap, rc::Rc, cell::{RefCell, Ref, RefMut}};
#[derive(Default, Debug)]
pub struct Resources {
values: HashMap<TypeId, Rc<RefCell<dyn Any>>>
}
impl Resources {
pub fn new() -> Self {
Self::default()
}
pub fn add<T: Any>(&mut self, res: T) {
self.values.insert(TypeId::of::<T>(), Rc::new(RefCell::new(res)));
}
pub fn get_ref<T: Any>(&self) -> eyre::Result<Ref<T>> {
let type_id = TypeId::of::<T>();
if let Some(data) = self.values.get(&type_id) {
let rf = data.as_ref();
let borrow = rf.borrow();
Ok(std::cell::Ref::map(borrow, |any| any.downcast_ref::<T>().unwrap()))
} else {
Err(ResourcesError::NonexistentResourceError.into())
}
}
pub fn get_mut<T: Any>(&self) -> eyre::Result<RefMut<T>> {
if let Some(data) = self.values.get(&TypeId::of::<T>()) {
let rf = data.as_ref();
let borrow = rf.borrow_mut();
Ok(RefMut::map(borrow, |any| any.downcast_mut::<T>().unwrap()))
} else {
Err(ResourcesError::NonexistentResourceError.into())
}
}
pub fn delete<T: Any>(&mut self) -> eyre::Result<T> {
if let Some(data) = self.values.remove(&TypeId::of::<T>())
{
Ok(
RefCell::into_inner(Rc::try_unwrap(downcast_t::<T>(data)).unwrap_or_else(|_| panic!("When removing resource it somehow failed to have the correct type, causing a segfault. bad, very bad")))
)
} else {
Err(ResourcesError::NonexistentResourceError.into())
}
}
}
fn downcast_t<T: Any>(
rc: Rc<RefCell<dyn Any>>,
) -> Rc<RefCell<T>> {
unsafe {
fn _sanity_check(rc: Rc<RefCell<impl Any>>)
-> Rc<RefCell<dyn Any>>
{
rc
}
Rc::from_raw(Rc::into_raw(rc) as *const RefCell<T>)
}
}
#[derive(thiserror::Error, Debug)]
pub enum ResourcesError {
#[error("Attempt to access non existent resource.")]
NonexistentResourceError,
}
impl std::fmt::Display for Resources {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{self:#?}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn add_resource() {
let mut resources = Resources::new();
let thing = Thing(12);
resources.add(thing);
let retreived_thing = resources.values.get(&TypeId::of::<Thing>()).unwrap();
let thing2 = std::cell::Ref::map(retreived_thing.as_ref().borrow(), |any| any.downcast_ref::<Thing>().unwrap());
assert_eq!(thing2.0, 12);
}
#[test]
fn get_resource_mut() {
let mut resources = Resources::new();
let thing = Thing(12);
resources.add(thing);
{
let mut other = resources.get_mut::<Thing>().unwrap();
other.0 = 129;
}
let other = resources.get_ref::<Thing>().unwrap();
assert_eq!(other.0, 129);
}
#[test]
fn get_resource() {
let mut resources = Resources::new();
resources.add(Thing(12));
let thing = resources.get_ref::<Thing>().unwrap();
assert_eq!(thing.0, 12);
}
#[test]
fn delete_resource() -> eyre::Result<()> {
let mut resources = init_resources();
resources.delete::<Thing>()?;
assert!(resources.get_ref::<Thing>().is_err());
assert!(!resources.values.contains_key(&TypeId::of::<Thing>()));
Ok(())
}
fn init_resources() -> Resources {
let mut res = Resources::new();
res.add(Thing(10));
res
}
#[derive(Debug, PartialEq)]
struct Thing(i32);
}