diff options
Diffstat (limited to 'mingling_core/src/asset')
| -rw-r--r-- | mingling_core/src/asset/chain.rs | 1 | ||||
| -rw-r--r-- | mingling_core/src/asset/chain/error.rs | 22 | ||||
| -rw-r--r-- | mingling_core/src/asset/core_invokes.rs | 268 | ||||
| -rw-r--r-- | mingling_core/src/asset/dispatcher.rs | 6 | ||||
| -rw-r--r-- | mingling_core/src/asset/global_resource.rs | 100 | ||||
| -rw-r--r-- | mingling_core/src/asset/lazy_resource.rs | 50 | ||||
| -rw-r--r-- | mingling_core/src/asset/metadata.rs | 14 | ||||
| -rw-r--r-- | mingling_core/src/asset/node.rs | 8 |
8 files changed, 386 insertions, 83 deletions
diff --git a/mingling_core/src/asset/chain.rs b/mingling_core/src/asset/chain.rs index 423e218..bd504d0 100644 --- a/mingling_core/src/asset/chain.rs +++ b/mingling_core/src/asset/chain.rs @@ -12,6 +12,7 @@ pub trait Chain<G> { #[cfg(feature = "async")] fn proc(p: Self::Previous) -> impl Future<Output = ChainProcess<G>> + Send; + /// Process the previous value and return a future that resolves to a [`ChainProcess<G>`](./enum.ChainProcess.html) #[cfg(not(feature = "async"))] fn proc(p: Self::Previous) -> ChainProcess<G>; } diff --git a/mingling_core/src/asset/chain/error.rs b/mingling_core/src/asset/chain/error.rs index ad64195..bb5f679 100644 --- a/mingling_core/src/asset/chain/error.rs +++ b/mingling_core/src/asset/chain/error.rs @@ -13,8 +13,8 @@ pub enum ChainProcessError { impl std::fmt::Display for ChainProcessError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - ChainProcessError::Other(s) => write!(f, "Other error: {s}"), - ChainProcessError::IO(e) => write!(f, "IO error: {e}"), + Self::Other(s) => write!(f, "Other error: {s}"), + Self::IO(e) => write!(f, "IO error: {e}"), } } } @@ -22,15 +22,15 @@ impl std::fmt::Display for ChainProcessError { impl std::error::Error for ChainProcessError { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { match self { - ChainProcessError::IO(e) => Some(e), - ChainProcessError::Other(_) => None, + Self::IO(e) => Some(e), + Self::Other(_) => None, } } } impl From<std::io::Error> for ChainProcessError { fn from(e: std::io::Error) -> Self { - ChainProcessError::IO(e) + Self::IO(e) } } @@ -38,17 +38,15 @@ impl From<ProgramInternalExecuteError> for ChainProcessError { fn from(value: ProgramInternalExecuteError) -> Self { match value { ProgramInternalExecuteError::DispatcherNotFound => { - ChainProcessError::Other("DispatcherNotFound".into()) + Self::Other("DispatcherNotFound".into()) } ProgramInternalExecuteError::RendererNotFound(r) => { - ChainProcessError::Other(format!("RendererNotFound: {r}")) - } - ProgramInternalExecuteError::Other(e) => ChainProcessError::Other(e), - ProgramInternalExecuteError::IO(e) => { - ChainProcessError::Other(format!("IOError: {e:?}")) + Self::Other(format!("RendererNotFound: {r}")) } + ProgramInternalExecuteError::Other(e) => Self::Other(e), + ProgramInternalExecuteError::IO(e) => Self::Other(format!("IOError: {e:?}")), ProgramInternalExecuteError::REPLPanic(program_panic) => { - ChainProcessError::Other(format!("REPLPanic: {program_panic}")) + Self::Other(format!("REPLPanic: {program_panic}")) } } } diff --git a/mingling_core/src/asset/core_invokes.rs b/mingling_core/src/asset/core_invokes.rs new file mode 100644 index 0000000..07e5092 --- /dev/null +++ b/mingling_core/src/asset/core_invokes.rs @@ -0,0 +1,268 @@ +use std::marker::PhantomData; + +use crate::{ + AnyOutput, ChainProcess, Grouped, NextProcess, ProgramCollect, RenderResult, ResourceMarker, +}; + +/// Type used to invoke Renderers in Mingling +/// +/// It is marked as `#[non_exhaustive]` and all internal fields are private. +/// This type can only be implicitly created by the hidden external API +/// `__resource_marker_default` of `ResourceMarker`. +/// +/// ```rust,ignore +/// // You can inject other renderers as types into the current function +/// // | +/// #[renderer(buffer)] // vvvvvvvvvvvvvvvvvvvv +/// fn render_foo(_: ResultFoo, renderer: &RendererInvoker<Bar>) { +/// let bar = Bar::default(); +/// r_append!(renderer.invoke(bar)); +/// } +/// ``` +#[non_exhaustive] +pub struct RendererInvoker<T> { + phantom: PhantomData<T>, + create_by_res_injection: bool, +} + +impl<T> RendererInvoker<T> +where + T: Send, +{ + /// Invoke the renderer with the given value. + /// + /// This function triggers the rendering pipeline for the provided value `T`. + /// It can only be executed when the `RendererInvoker` was created by the resource injection system + /// (i.e., via `__resource_marker_default`). If the invoker was created manually or cloned outside + /// the injection context, calling this method will panic. + /// + /// # Panics + /// + /// Panics if the `RendererInvoker` was not created by the resource injection system. + /// + /// # Hook + /// + /// It will not execute any program hooks, because this type is used for **bypassing** or **reusing**, not for flow control. + pub fn invoke<C>(&self, value: T) -> RenderResult + where + C: ProgramCollect<Enum = C> + 'static, + T: Grouped<C>, + { + assert!( + self.create_by_res_injection, + "The current RendererInvoker was not created by the resource injection system, so it cannot be executed!" + ); + C::render(AnyOutput::new(value)) + } +} + +impl<T> ResourceMarker for RendererInvoker<T> { + fn __resource_marker_clone(&self) -> Self { + Self { + phantom: PhantomData, + create_by_res_injection: self.create_by_res_injection, + } + } + + fn __resource_marker_default() -> Self { + // Create RendererInvoker with `create_by_res_injection` marked as true + // + // Reason: RendererInvoker is designed to only be created through resource injection. + // When the resource injection does not find a corresponding value, + // this method will be used to generate a default value to pass in. + Self { + phantom: PhantomData, + create_by_res_injection: true, + } + } + + fn __resource_marker_modify<C>(f: impl FnOnce(&mut Self)) + where + C: ProgramCollect<Enum = C> + 'static, + { + // DO NOTHING + // + // When ResourceMarker is asked to modify, it should not execute anything. + let _ = f; + } +} + +/// Type used to invoke Chain in Mingling +/// +/// It is marked as `#[non_exhaustive]` and all internal fields are private. +/// This type can only be implicitly created by the hidden external API +/// `__resource_marker_default` of `ResourceMarker`. +/// +/// ```rust,ignore +/// // You can inject other chain as types into the current function +/// // | +/// #[chain] // vvvvvvvvvvvvvvvvvvvvv +/// fn handle_foo(_: EntryFoo, chain: &ChainInvoker<StateBar>) { +/// let bar = Bar::default(); +/// let next = chain.invoke_once(bar); +/// } +/// ``` +#[non_exhaustive] +pub struct ChainInvoker<T> { + phantom: PhantomData<T>, + create_by_res_injection: bool, +} + +impl<T> ChainInvoker<T> +where + T: Send, +{ + /// Execute one step of the chain with the given value, returning the next state. + /// + /// This function performs **only a single step** of chain execution — it invokes the + /// current handler for value `T` and returns the next [`ChainProcess`] state, without + /// automatically continuing the chain. The caller is responsible for proceeding through + /// subsequent steps based on the returned state. + /// + /// It can only be executed when the `ChainInvoker` was created by the resource injection system + /// (i.e., via `__resource_marker_default`). If the invoker was created manually or cloned outside + /// the injection context, calling this method will panic. + /// + /// # Special Behavior + /// + /// If no chain exists for this type, it will convert itself into a `ChainProcess` that routes to the chain and return it. + /// + /// # Panics + /// + /// Panics if the `ChainInvoker` was not created by the resource injection system. + /// + /// # Hooks + /// + /// It will not execute any program hooks, because this type is used for **bypassing** or **reusing**, not for flow control. + #[might_be_async::func] + pub fn invoke_once<C>(&self, value: T) -> ChainProcess<C> + where + C: ProgramCollect<Enum = C> + 'static, + T: Grouped<C>, + { + self.pre_check(); + + let any = AnyOutput::new(value); + + if C::has_chain(&any) { + might_be_async::invoke!(C::do_chain(any)) + } else { + ChainProcess::Ok((any, NextProcess::Chain)) + } + } + + /// Continuously execute the chain until it is routed to a renderer or can no longer continue + /// + /// This function can only be called when the `ChainInvoker` was created by the resource injection system + /// (i.e., via `__resource_marker_default`). If the invoker was created manually or cloned outside + /// the injection context, calling this method will panic. + /// + /// # Panics + /// + /// Panics if the `ChainInvoker` was not created by the resource injection system. + /// + /// # Hooks + /// + /// It will not execute any program hooks, because this type is used for **bypassing** or **reusing**, not for flow control. + #[might_be_async::func] + pub fn invoke_to_last<C>(&self, value: T) -> ChainProcess<C> + where + C: ProgramCollect<Enum = C> + 'static, + T: Grouped<C>, + { + self.pre_check(); + + let mut current = might_be_async::invoke!(C::do_chain(AnyOutput::new(value))); + + loop { + match current { + ChainProcess::Ok((any, NextProcess::Chain)) => { + if C::has_chain(&any) { + current = might_be_async::invoke!(C::do_chain(any)); + } else { + // If the next step of this type does not have a Chain, reconstruct it back + return ChainProcess::Ok((any, NextProcess::Chain)); + } + } + _ => return current, + } + } + } + + /// Continuously execute the chain until it is rendered into a `RenderResult` + /// + /// This function can only be called when the `ChainInvoker` was created by the resource injection system + /// (i.e., via `__resource_marker_default`). If the invoker was created manually or cloned outside + /// the injection context, calling this method will panic. + /// + /// # Special Behavior + /// + /// If an error occurs during rendering, or the result type does not contain a renderer, it will be rendered as an empty `RenderResult` + /// + /// # Panics + /// + /// Panics if the `ChainInvoker` was not created by the resource injection system. + /// + /// # Hooks + /// + /// It will not execute any program hooks, because this type is used for **bypassing** or **reusing**, not for flow control. + #[might_be_async::func] + pub fn invoke_to_result<C>(&self, value: T) -> RenderResult + where + C: ProgramCollect<Enum = C> + 'static, + T: Grouped<C>, + { + self.pre_check(); + + let last = might_be_async::invoke!(self.invoke_to_last(value)); + + match last { + ChainProcess::Err(_) => RenderResult::new(), + ChainProcess::Ok((any, _)) => { + if C::has_renderer(&any) { + C::render(any) + } else { + RenderResult::new() + } + } + } + } + + fn pre_check(&self) { + assert!( + self.create_by_res_injection, + "The current ChainInvoker was not created by the resource injection system, so it cannot be executed!" + ); + } +} + +impl<T> ResourceMarker for ChainInvoker<T> { + fn __resource_marker_clone(&self) -> Self { + Self { + phantom: PhantomData, + create_by_res_injection: self.create_by_res_injection, + } + } + + fn __resource_marker_default() -> Self { + // Create ChainInvoker with `create_by_res_injection` marked as true + // + // Reason: ChainInvoker is designed to only be created through resource injection. + // When the resource injection does not find a corresponding value, + // this method will be used to generate a default value to pass in. + Self { + phantom: PhantomData, + create_by_res_injection: true, + } + } + + fn __resource_marker_modify<C>(f: impl FnOnce(&mut Self)) + where + C: ProgramCollect<Enum = C> + 'static, + { + // DO NOTHING + // + // When ResourceMarker is asked to modify, it should not execute anything. + let _ = f; + } +} diff --git a/mingling_core/src/asset/dispatcher.rs b/mingling_core/src/asset/dispatcher.rs index 01c9ccf..cb0987d 100644 --- a/mingling_core/src/asset/dispatcher.rs +++ b/mingling_core/src/asset/dispatcher.rs @@ -38,7 +38,7 @@ where note = "When the `dispatch_tree` feature is enabled, the `dispatcher` field no longer exists inside Program. All types are collected at compile time by the `gen_program!()` macro, so the `with_dispatcher` function is no longer needed" ) )] - pub fn with_dispatcher<Disp>(&mut self, dispatcher: Disp) + pub fn with_dispatcher<Disp>(&mut self, dispatcher: Disp) -> &mut Self where Disp: Dispatcher<C> + Send + Sync + 'static, { @@ -50,6 +50,7 @@ where { let _ = dispatcher; } + self } /// Add some dispatchers to the program. @@ -59,7 +60,7 @@ where note = "When the `dispatch_tree` feature is enabled, the `dispatcher` field no longer exists inside Program. All types are collected at compile time by the `gen_program!()` macro, so the `with_dispatcher` function is no longer needed" ) )] - pub fn with_dispatchers<D>(&mut self, dispatchers: D) + pub fn with_dispatchers<D>(&mut self, dispatchers: D) -> &mut Self where D: Into<Dispatchers<C>>, { @@ -72,6 +73,7 @@ where { let _ = dispatchers; } + self } } 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(), diff --git a/mingling_core/src/asset/lazy_resource.rs b/mingling_core/src/asset/lazy_resource.rs index 918aeb2..e8ec55b 100644 --- a/mingling_core/src/asset/lazy_resource.rs +++ b/mingling_core/src/asset/lazy_resource.rs @@ -54,12 +54,10 @@ impl<T: Send + Sync + 'static> LazyRes<T> { /// dropped. If the resource has not yet been initialized, the callback is stored /// and will be invoked once initialization happens *and* the `LazyRes` is later /// dropped. + #[must_use] pub fn with_on_drop(mut self, on_drop: impl FnOnce(T) + Send + Sync + 'static) -> Self { match &mut self.inner { - LazyInner::Uninit(_, existing_opt) => { - *existing_opt = Some(Box::new(on_drop)); - } - LazyInner::Init(_, existing_opt) => { + LazyInner::Uninit(_, existing_opt) | LazyInner::Init(_, existing_opt) => { *existing_opt = Some(Box::new(on_drop)); } } @@ -72,17 +70,14 @@ impl<T: Send + Sync + 'static> LazyRes<T> { /// upon initialization. pub fn set_on_drop(&mut self, on_drop: impl FnOnce(T) + Send + Sync + 'static) { match &mut self.inner { - LazyInner::Uninit(_, existing_opt) => { - *existing_opt = Some(Box::new(on_drop)); - } - LazyInner::Init(_, existing_opt) => { + LazyInner::Uninit(_, existing_opt) | LazyInner::Init(_, existing_opt) => { *existing_opt = Some(Box::new(on_drop)); } } } /// Returns `true` if the resource has been initialized. - pub fn is_initialized(&self) -> bool { + pub const fn is_initialized(&self) -> bool { matches!(&self.inner, LazyInner::Init(_, _)) } @@ -115,7 +110,7 @@ impl<T: Send + Sync + 'static> LazyRes<T> { self.force_init(); match &self.inner { LazyInner::Init(t, _) => t, - _ => unreachable!(), + LazyInner::Uninit(..) => unreachable!(), } } @@ -125,7 +120,7 @@ impl<T: Send + Sync + 'static> LazyRes<T> { self.force_init(); match &mut self.inner { LazyInner::Init(t, _) => t, - _ => unreachable!(), + LazyInner::Uninit(..) => unreachable!(), } } @@ -157,6 +152,10 @@ impl<T: Send + Sync + 'static> LazyRes<T> { /// /// If the resource has not been initialized, the initializer is called first. /// This is different from `into_inner()` which returns `None` if uninitialized. + /// + /// # Panics + /// + /// Panics if the resource has not been initialized. pub fn unwrap(mut self) -> T { match std::mem::replace( &mut self.inner, @@ -179,7 +178,7 @@ impl<T: Send + Sync + 'static> LazyRes<T> { LazyInner::Uninit(Box::new(|| unreachable!()), None), ) { LazyInner::Init(t, _) => t, - _ => unreachable!(), + LazyInner::Uninit(..) => unreachable!(), } } } @@ -216,10 +215,7 @@ impl<T: Send + Sync + 'static> Drop for LazyRes<T> { } } -impl<T: Send + Sync + 'static> Default for LazyRes<T> -where - T: Default, -{ +impl<T: Send + Sync + Default + 'static> Default for LazyRes<T> { /// Creates an uninitialized `LazyRes<T>` whose initializer returns `T::default()`. fn default() -> Self { Self::new(|| T::default()) @@ -237,6 +233,7 @@ impl<T: Send + Sync + 'static> From<T> for LazyRes<T> { impl<T: Send + Sync + 'static> LazyRes<T> { /// Creates a lazily initialized resource using `T::default()` as the initializer. + #[must_use] pub fn lazy_default() -> Self where T: Default, @@ -256,6 +253,7 @@ impl<T: Send + Sync + 'static> LazyRes<T> { /// create a corresponding `LazyRes<T>`. pub trait LazyInit: Send + Sync + 'static { /// Creates a lazily initialized resource for this type using `Default` as the initializer. + #[must_use] fn lazy_default() -> LazyRes<Self> where Self: Default, @@ -274,13 +272,10 @@ pub trait LazyInit: Send + Sync + 'static { impl<T: Send + Sync + 'static> LazyInit for T {} -impl<T: Send + Sync + 'static> ResourceMarker for LazyRes<T> -where - T: Default + Clone, -{ +impl<T: Send + Sync + 'static + Default + Clone> ResourceMarker for LazyRes<T> { /// Clones the lazy resource. The cloned resource retains any initialized value, /// but the initializer is reset to `T::default()`. - fn res_clone(&self) -> Self { + fn __resource_marker_clone(&self) -> Self { match &self.inner { LazyInner::Init(t, _) => Self { inner: LazyInner::Init(t.clone(), None), @@ -290,15 +285,12 @@ where } /// Returns a default lazy resource (uninitialized, using `T::default()` as the initializer). - fn res_default() -> Self - where - T: Default, - { + fn __resource_marker_default() -> Self { Self::default() } /// Modifies the current lazy resource via the `this` context provided by `C`. - fn modify<C>(f: impl FnOnce(&mut Self)) + fn __resource_marker_modify<C>(f: impl FnOnce(&mut Self)) where C: ProgramCollect<Enum = C> + 'static, { @@ -583,7 +575,7 @@ mod tests { fn res_clone_of_initialized_clones_value() { let mut r = LazyRes::new(|| vec![1, 2, 3]); r.get_ref(); - let cloned = r.res_clone(); + let cloned = r.__resource_marker_clone(); assert!(cloned.is_initialized()); assert_eq!(cloned.into_inner(), Some(vec![1, 2, 3])); } @@ -591,14 +583,14 @@ mod tests { #[test] fn res_clone_of_uninitialized_creates_default() { let r: LazyRes<Vec<i32>> = LazyRes::new(|| vec![1, 2, 3]); - let cloned = r.res_clone(); + let cloned = r.__resource_marker_clone(); // The source is uninitialized, so res_clone returns a default lazy assert!(!cloned.is_initialized()); } #[test] fn res_default_returns_uninitialized() { - let r: LazyRes<i32> = LazyRes::<i32>::res_default(); + let r: LazyRes<i32> = LazyRes::<i32>::__resource_marker_default(); assert!(!r.is_initialized()); } diff --git a/mingling_core/src/asset/metadata.rs b/mingling_core/src/asset/metadata.rs new file mode 100644 index 0000000..996b34c --- /dev/null +++ b/mingling_core/src/asset/metadata.rs @@ -0,0 +1,14 @@ +/// Provides metadata for an Entry. +/// +/// Any type can be attached to an Entry as metadata, allowing the program to +/// carry compile-time-typed, arbitrary description data alongside each +/// registered entry. The [`Metadata`] trait bridges an Entry type (`Self`) to +/// an arbitrary metadata type `B`. +/// +/// It is recommended to use the `#[metadata(Entry)]` attribute macro from +/// [mingling_macros](https://crates.io/crates/mingling_macros) to implement this +/// trait and register the entry via `register_metadata!`. +pub trait Metadata<B> { + /// Initializes and returns the metadata value of type `B` for this entry. + fn init_metadata() -> B; +} diff --git a/mingling_core/src/asset/node.rs b/mingling_core/src/asset/node.rs index caf34ec..b9002b6 100644 --- a/mingling_core/src/asset/node.rs +++ b/mingling_core/src/asset/node.rs @@ -12,24 +12,24 @@ pub struct Node { impl Node { /// Append a new part to the node path. #[must_use] - pub fn join(self, node: impl Into<String>) -> Node { + pub fn join(self, node: impl Into<String>) -> Self { let mut new_node = self.node; new_node.push(node.into()); - Node { node: new_node } + Self { node: new_node } } } impl From<&str> for Node { fn from(s: &str) -> Self { let node = s.split('.').map(|part| kebab_case!(part)).collect(); - Node { node } + Self { node } } } impl From<String> for Node { fn from(s: String) -> Self { let node = s.split('.').map(|part| kebab_case!(part)).collect(); - Node { node } + Self { node } } } |
