diff options
Diffstat (limited to 'mingling_core/src/asset/global_resource.rs')
| -rw-r--r-- | mingling_core/src/asset/global_resource.rs | 100 |
1 files changed, 64 insertions, 36 deletions
diff --git a/mingling_core/src/asset/global_resource.rs b/mingling_core/src/asset/global_resource.rs index 29e1136..f37edd0 100644 --- a/mingling_core/src/asset/global_resource.rs +++ b/mingling_core/src/asset/global_resource.rs @@ -6,17 +6,24 @@ use std::{ use crate::{ChainProcess, Program, ProgramCollect, this}; -pub(crate) type GlobalResources = Arc<Mutex<HashMap<TypeId, Box<dyn Any + Sync + Send>>>>; +/// A thread-safe, type-erased container for storing global resources keyed by their type. +/// +/// Use `Program::with_resource` to insert resources and `Program::res` to retrieve them. +pub type GlobalResources = Arc<Mutex<HashMap<TypeId, Box<dyn Any + Sync + Send>>>>; impl<C> Program<C> where C: ProgramCollect<Enum = C>, { /// Insert a resource of the given type, cloning the provided value into the store - pub fn with_resource<Res: 'static + Send + Sync + ResourceMarker>(&mut self, res: Res) { + pub fn with_resource<Res: 'static + Send + Sync + ResourceMarker>( + &mut self, + res: Res, + ) -> &mut Self { if let Ok(mut guard) = self.resources.lock() { guard.insert(TypeId::of::<Res>(), Box::new(Arc::new(res))); } + self } /// Modify a resource by type, applying a closure to the resource if present @@ -34,7 +41,7 @@ where { let mut new_res = match Arc::try_unwrap(std::mem::take(arc_res)) { Ok(val) => val, - Err(arc) => (*arc).res_clone(), + Err(arc) => (*arc).__resource_marker_clone(), }; let r = f(&mut new_res); *arc_res = Arc::new(new_res); @@ -53,7 +60,7 @@ where Res: 'static + Default + ResourceMarker + Send + Sync, { let Ok(mut guard) = self.resources.lock() else { - let mut default_res = Res::res_default(); + let mut default_res = Res::__resource_marker_default(); return f(&mut default_res); }; if let Some(arc_res) = guard @@ -62,13 +69,13 @@ where { let mut new_res = match Arc::try_unwrap(std::mem::take(arc_res)) { Ok(val) => val, - Err(arc) => (*arc).res_clone(), + Err(arc) => (*arc).__resource_marker_clone(), }; let r = f(&mut new_res); *arc_res = Arc::new(new_res); r } else { - let mut default_res = Res::res_default(); + let mut default_res = Res::__resource_marker_default(); f(&mut default_res) } } @@ -81,19 +88,18 @@ where #[must_use] pub fn __extract_res_mut<Res: 'static + Default + ResourceMarker + Send + Sync>(&self) -> Res { let Ok(mut guard) = self.resources.lock() else { - return Res::res_default(); + return Res::__resource_marker_default(); }; - if let Some(arc_res) = guard + guard .get_mut(&TypeId::of::<Res>()) .and_then(|a| a.downcast_mut::<Arc<Res>>()) - { - match Arc::try_unwrap(std::mem::take(arc_res)) { - Ok(val) => val, - Err(arc) => (*arc).res_clone(), - } - } else { - Res::res_default() - } + .map_or_else( + Res::__resource_marker_default, + |arc_res| match Arc::try_unwrap(std::mem::take(arc_res)) { + Ok(val) => val, + Err(arc) => (*arc).__resource_marker_clone(), + }, + ) } /// Internal syntax for the `&mut MyResource` syntax of async #[chain], do not use directly. @@ -112,18 +118,23 @@ where let guard = self.resources.lock().ok()?; let boxed_any = guard.get(&TypeId::of::<Res>())?; let arc_res = boxed_any.as_ref().downcast_ref::<Arc<Res>>()?; - Some(GlobalResource::from(Arc::clone(arc_res))) + let result = GlobalResource::from(Arc::clone(arc_res)); + drop(guard); + Some(result) } - /// Get a resource by type, returning `GlobalResource<Res>` if present + /// Get a resource by type, returning `GlobalResource<Res>` if present. + /// + /// If the resource is not present, returns the provided [`ChainProcess`] as an `Err`. + /// + /// # Errors + /// + /// Returns `Err(route)` when the resource of type `Res` is not present in the store. pub fn res_or_route<Res: 'static + Send + Sync>( &self, route: ChainProcess<C>, ) -> Result<GlobalResource<Res>, ChainProcess<C>> { - match self.res() { - Some(r) => Ok(r), - None => Err(route), - } + self.res().map_or_else(|| Err(route), Ok) } /// Get a resource by type, returning `GlobalResource<Res>` or inserting a default @@ -132,7 +143,7 @@ where &self, ) -> GlobalResource<Res> { self.res() - .unwrap_or_else(|| GlobalResource::from(Arc::new(Res::res_default()))) + .unwrap_or_else(|| GlobalResource::from(Arc::new(Res::__resource_marker_default()))) } } @@ -172,24 +183,38 @@ impl<ResType: 'static + Send + Sync> AsRef<ResType> for GlobalResource<ResType> /// Resource marker trait, types that implement the Clone and Default traits can be considered as resources pub trait ResourceMarker { + /// Clone the resource. This is an internal method used by the resource injection system + /// and should not be called directly by user code. #[must_use] - fn res_clone(&self) -> Self; - fn res_default() -> Self; - fn modify<C>(f: impl FnOnce(&mut Self)) + #[doc(hidden)] + fn __resource_marker_clone(&self) -> Self; + + /// Create a default instance of the resource. This is an internal method used by the + /// resource injection system and should not be called directly by user code. + #[doc(hidden)] + fn __resource_marker_default() -> Self; + + /// Modify the resource using a closure. This is an internal method used by the resource + /// injection system and should not be called directly by user code. + #[doc(hidden)] + fn __resource_marker_modify<C>(f: impl FnOnce(&mut Self)) where C: ProgramCollect<Enum = C> + 'static; } -impl<T: Default + Clone + Send + Sync + 'static> ResourceMarker for T { - fn res_clone(&self) -> Self { +impl<T> ResourceMarker for T +where + T: Default + Clone + Send + Sync + 'static, +{ + fn __resource_marker_clone(&self) -> Self { Clone::clone(self) } - fn res_default() -> Self { + fn __resource_marker_default() -> Self { Default::default() } - fn modify<C>(f: impl FnOnce(&mut Self)) + fn __resource_marker_modify<C>(f: impl FnOnce(&mut Self)) where C: ProgramCollect<Enum = C> + 'static, { @@ -223,38 +248,41 @@ mod tests { #[test] fn resource_marker_i32_res_clone() { let val = 42i32; - let cloned = val.res_clone(); + let cloned = val.__resource_marker_clone(); assert_eq!(cloned, 42); } #[test] fn resource_marker_i32_res_default() { - assert_eq!(<i32 as ResourceMarker>::res_default(), 0i32); + assert_eq!(<i32 as ResourceMarker>::__resource_marker_default(), 0i32); } #[test] fn resource_marker_string_res_clone() { let val = "hello".to_string(); - let cloned = val.res_clone(); + let cloned = val.__resource_marker_clone(); assert_eq!(cloned, "hello"); } #[test] fn resource_marker_string_res_default() { - assert_eq!(<String as ResourceMarker>::res_default(), ""); + assert_eq!(<String as ResourceMarker>::__resource_marker_default(), ""); } #[test] fn resource_marker_vec_res_clone() { let val = vec![1, 2, 3]; - let cloned = val.res_clone(); + let cloned = val.__resource_marker_clone(); assert_eq!(cloned, vec![1, 2, 3]); } #[test] fn resource_marker_vec_res_default() { let empty: Vec<i32> = vec![]; - assert_eq!(<Vec<i32> as ResourceMarker>::res_default(), empty); + assert_eq!( + <Vec<i32> as ResourceMarker>::__resource_marker_default(), + empty + ); } // Note: Tests for Program::with_resource, res(), res_or_route(), res_or_default(), |
