aboutsummaryrefslogtreecommitdiff
path: root/mingling_core/src/asset/global_resource.rs
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_core/src/asset/global_resource.rs')
-rw-r--r--mingling_core/src/asset/global_resource.rs100
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(),