use std::{ any::{Any, TypeId}, collections::HashMap, sync::{Arc, Mutex}, }; use crate::{ChainProcess, Program, ProgramCollect}; pub type GlobalResources = Arc>>>; impl Program where C: ProgramCollect, { /// Insert a resource of the given type, cloning the provided value into the store pub fn with_resource(&mut self, res: Res) { if let Ok(mut guard) = self.resources.lock() { guard.insert(TypeId::of::(), Box::new(Arc::new(res))); } } /// Modify a resource by type, applying a closure to the resource if present pub fn modify_res(&self, f: impl FnOnce(&mut Res)) where Res: 'static + Default + ResourceMarker + Send + Sync, { let mut guard = match self.resources.lock() { Ok(guard) => guard, Err(_) => { return; } }; if let Some(arc_res) = guard .get_mut(&TypeId::of::()) .and_then(|a| a.downcast_mut::>()) { let mut new_res = match Arc::try_unwrap(std::mem::take(arc_res)) { Ok(val) => val, Err(arc) => (*arc).res_clone(), }; f(&mut new_res); *arc_res = Arc::new(new_res); } } /// Get an resources by type, returning `Res` if present pub fn res(&self) -> Option> { let guard = self.resources.lock().ok()?; let boxed_any = guard.get(&TypeId::of::())?; let arc_res = boxed_any.as_ref().downcast_ref::>()?; Some(GlobalResource::from(Arc::clone(arc_res))) } /// Get a resource by type, returning `GlobalResource` if present pub fn res_or_route( &self, route: ChainProcess, ) -> Result, ChainProcess> { match self.res() { Some(r) => Ok(r), None => Err(route), } } /// Get a resource by type, returning `GlobalResource` or inserting a default pub fn res_or_default( &self, ) -> GlobalResource { self.res() .unwrap_or_else(|| GlobalResource::from(Arc::new(Res::res_default()))) } } /// Global assets for storing Program global state information pub struct GlobalResource { res_arc: Arc, } impl GlobalResource { /// Create a new `GlobalAsset` from an `AssetType` value. pub fn new(res: ResType) -> Self { Self { res_arc: Arc::new(res), } } } impl From> for GlobalResource { fn from(arc: Arc) -> Self { Self { res_arc: arc } } } impl std::ops::Deref for GlobalResource { type Target = ResType; fn deref(&self) -> &Self::Target { &self.res_arc } } /// Resource marker trait, types that implement the Clone and Default traits can be considered as resources pub trait ResourceMarker { fn res_clone(&self) -> Self; fn res_default() -> Self; } impl ResourceMarker for T { fn res_clone(&self) -> Self { Clone::clone(self) } fn res_default() -> Self { Default::default() } }