use downcast_rs::{impl_downcast, Downcast};
use fxhash::FxHashMap;
use parking_lot::RwLock;
use std::any::TypeId;
use crate::{
entry::Entry,
error::{CantGetResource, NoSuchResource},
refs::{Ref, RefMut},
};
#[cfg(feature = "fetch")]
use crate::fetch::{CantFetch, Fetch};
pub trait Resource: Downcast + Send + Sync + 'static {}
impl<T> Resource for T where T: Send + Sync + 'static {}
impl_downcast!(Resource);
#[derive(Default)]
pub struct Resources {
resources: FxHashMap<TypeId, RwLock<Box<dyn Resource>>>,
}
fn downcast_resource<T: Resource>(resource: Box<dyn Resource>) -> T {
*resource
.downcast::<T>()
.unwrap_or_else(|_| panic!("downcasting resources should always succeed"))
}
impl Resources {
pub fn new() -> Self {
Self::default()
}
pub fn contains<T: Resource>(&self) -> bool {
self.resources.contains_key(&TypeId::of::<T>())
}
pub fn insert<T: Resource>(&mut self, resource: T) -> Option<T> {
self.resources
.insert(TypeId::of::<T>(), RwLock::new(Box::new(resource)))
.map(|resource| downcast_resource(resource.into_inner()))
}
pub fn remove<T: Resource>(&mut self) -> Option<T> {
self.resources
.remove(&TypeId::of::<T>())
.map(|resource| downcast_resource(resource.into_inner()))
}
pub fn entry<T: Resource>(&mut self) -> Entry<T> {
Entry::from_hash_map_entry(self.resources.entry(TypeId::of::<T>()))
}
pub fn get<T: Resource>(&self) -> Result<Ref<T>, CantGetResource> {
self.resources
.get(&TypeId::of::<T>())
.ok_or_else(|| NoSuchResource.into())
.and_then(|lock| Ref::from_lock(lock).map_err(|error| error.into()))
}
pub fn get_mut<T: Resource>(&self) -> Result<RefMut<T>, CantGetResource> {
self.resources
.get(&TypeId::of::<T>())
.ok_or_else(|| NoSuchResource.into())
.and_then(|lock| RefMut::from_lock(lock).map_err(|error| error.into()))
}
#[cfg(feature = "fetch")]
pub fn fetch<R>(&self) -> Result<<R as Fetch>::Refs, CantFetch>
where
for<'a> R: Fetch<'a>,
{
R::fetch(self)
}
}