aboutsummaryrefslogtreecommitdiff
path: root/mingling_core/src/asset
diff options
context:
space:
mode:
Diffstat (limited to 'mingling_core/src/asset')
-rw-r--r--mingling_core/src/asset/chain.rs1
-rw-r--r--mingling_core/src/asset/chain/error.rs22
-rw-r--r--mingling_core/src/asset/core_invokes.rs268
-rw-r--r--mingling_core/src/asset/dispatcher.rs6
-rw-r--r--mingling_core/src/asset/global_resource.rs100
-rw-r--r--mingling_core/src/asset/lazy_resource.rs50
-rw-r--r--mingling_core/src/asset/metadata.rs14
-rw-r--r--mingling_core/src/asset/node.rs8
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 }
}
}