use std::{any::{Any, TypeId}, marker::PhantomData, sync::{Arc, RwLock}}; use lyra_ecs::Component; use crate::{loader::LoaderError, lyra_engine}; use uuid::Uuid; use crate::ResourceStorage; /// A trait that that each resource type should implement. pub trait ResourceData: Send + Sync + Any + 'static { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; fn type_id(&self) -> TypeId; } impl ResourceData for T { fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } fn type_id(&self) -> TypeId { TypeId::of::() } } pub enum ResourceState { Loading, Error(Arc), Ready(Box), } pub struct ResourceDataRef<'a, T> { guard: std::sync::RwLockReadGuard<'a, Resource>, } impl<'a, T: 'static> std::ops::Deref for ResourceDataRef<'a, T> { type Target = T; fn deref(&self) -> &Self::Target { match &self.guard.state { ResourceState::Ready(d) => { // for some reason, if I didn't use `.as_ref`, the downcast would fail. let d = d.as_ref().as_any(); d.downcast_ref::().unwrap() }, _ => unreachable!() // ResHandler::data_ref shouldn't allow this to run } } } pub struct Resource { pub(crate) version: usize, pub(crate) state: ResourceState, uuid: Uuid, path: Option, pub(crate) is_watched: bool, _marker: PhantomData, } /// A handle to a resource. /// /// # Note /// This struct has an inner [`RwLock`] to the resource data, so most methods may be blocking. /// However, the only times it will be blocking is if another thread is reloading the resource /// and has a write lock on the data. This means that most of the time, it is not blocking. #[derive(Component)] pub struct ResHandle { pub(crate) data: Arc>>, } impl Clone for ResHandle { fn clone(&self) -> Self { Self { data: self.data.clone() } } } impl ResHandle { pub fn new_loading(path: Option<&str>) -> Self { let res_version = Resource { version: 0, path: path.map(str::to_string), state: ResourceState::Loading, uuid: Uuid::new_v4(), is_watched: false, _marker: PhantomData:: }; Self { data: Arc::new(RwLock::new(res_version)), } } /// Create the resource with data, its assumed the state is `Ready` pub fn new_ready(path: Option<&str>, data: T) -> Self { let res_version = Resource { version: 0, path: path.map(str::to_string), state: ResourceState::Ready(Box::new(data)), uuid: Uuid::new_v4(), is_watched: false, _marker: PhantomData:: }; Self { data: Arc::new(RwLock::new(res_version)), } } /// Returns a boolean indicating if this resource's path is being watched. pub fn is_watched(&self) -> bool { let d = self.data.read().expect("Resource mutex was poisoned!"); d.is_watched } /// Returns a boolean indicating if this resource is loaded pub fn is_loaded(&self) -> bool { let d = self.data.read().expect("Resource mutex was poisoned!"); matches!(d.state, ResourceState::Ready(_)) } /// Returns the current state of the resource. /* pub fn state(&self) -> &ResourceState { let d = self.data.read().expect("Resource mutex was poisoned!"); &d.state } */ /// Returns the uuid of the resource. pub fn uuid(&self) -> Uuid { let d = self.data.read().expect("Resource mutex was poisoned!"); d.uuid } pub fn path(&self) -> Option { let d = self.data.read().expect("Resource mutex was poisoned!"); d.path.clone() } /// Retrieves the current version of the resource. This gets incremented when the resource /// is reloaded. pub fn version(&self) -> usize { let d = self.data.read().expect("Resource mutex was poisoned!"); d.version } /// Get a reference to the data in the resource pub fn data_ref<'a>(&'a self) -> Option> { if self.is_loaded() { let d = self.data.read().expect("Resource mutex was poisoned!"); Some(ResourceDataRef { guard: d }) } else { None } } } impl ResourceStorage for ResHandle { fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } fn as_arc_any(self: Arc) -> Arc { self.clone() } fn as_box_any(self: Box) -> Box { self } fn set_watched(&self, watched: bool) { let mut w = self.data.write().unwrap(); w.is_watched = watched; } fn version(&self) -> usize { self.version() } fn uuid(&self) -> Uuid { self.uuid() } fn path(&self) -> Option { self.path() } fn is_watched(&self) -> bool { self.is_watched() } fn is_loaded(&self) -> bool { self.is_loaded() } fn set_state(&self, new: ResourceState) { let mut d = self.data.write().expect("Resource mutex was poisoned!"); d.state = new; } }