diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-04-27 21:19:56 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-04-27 21:19:56 +0800 |
| commit | 2c32196bbc632411d4f6998a506ca262a805a666 (patch) | |
| tree | 62934fbda52b11bfff984030dbbe52a1926fb4ac | |
| parent | ad10b82c1e6785cbf88562117b1609905cfeb6dc (diff) | |
Add global resource system to Program
| -rw-r--r-- | CHANGELOG.md | 23 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | docs/res/changlog_examples/feat_program_res.rs | 50 | ||||
| -rw-r--r-- | mingling_core/src/asset.rs | 3 | ||||
| -rw-r--r-- | mingling_core/src/asset/global_resource.rs | 116 | ||||
| -rw-r--r-- | mingling_core/src/lib.rs | 1 | ||||
| -rw-r--r-- | mingling_core/src/program.rs | 11 | ||||
| -rw-r--r-- | mingling_macros/src/lib.rs | 1 |
8 files changed, 203 insertions, 3 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index 582cbdb..0997e27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -74,6 +74,29 @@ fn parse(prev: PickEntry) -> mingling::ChainProcess<ThisProgram> { } ``` +8. Added a resource system to `Program` for managing global resources [Details](docs/res/changlog_examples/feat_program_res.rs) + +```rust +// Define global resource +#[derive(Debug, Default, Clone)] +struct Global { + name: String, + age: i32, +} + +// Add global resource +program.with_resource(Global::default()); + +// Read the global resource +let global = this::<ThisProgram>().res_or_default::<Global>(); + +// Modify the global resource +this::<ThisProgram>().modify_res(|r: &mut Global| { + r.name = name; + r.age = age +}); +``` + #### **BREAKING CHANGES**: 1. **\[macros\]** Removed macro `dispatcher_render!` from `mingling_macros` @@ -11,7 +11,6 @@ members = [ "mingling_macros", ] exclude = [ - "mingling_cli", "dev_tools", "examples/example-async", "examples/example-basic", diff --git a/docs/res/changlog_examples/feat_program_res.rs b/docs/res/changlog_examples/feat_program_res.rs new file mode 100644 index 0000000..b3533f1 --- /dev/null +++ b/docs/res/changlog_examples/feat_program_res.rs @@ -0,0 +1,50 @@ +use mingling::{ + macros::{chain, dispatcher, gen_program, pack, r_println, renderer}, + parser::Picker, + this, +}; + +// Define a struct with Default and Clone derives +#[derive(Debug, Default, Clone)] +struct Global { + name: String, + age: i32, +} + +fn main() { + let mut program = ThisProgram::new(); + + // Add a global resource here + program.with_resource(Global::default()); + + program.exec(); +} + +dispatcher!("modify", ResModifyCommand => ResModifyEntry); + +pack!(DisplayGlobal = ()); + +#[chain] +fn modify(prev: ResModifyEntry) { + let (name, age) = Picker::<()>::new(prev.inner) + .pick::<String>("--name") + .pick::<i32>("--age") + .unpack_directly(); + + // Modify the global resource + this::<ThisProgram>().modify_res(|r: &mut Global| { + r.name = name; + r.age = age + }); + + DisplayGlobal::default() +} + +#[renderer] +fn render_global(_prev: DisplayGlobal) { + // Read the global resource + let global = this::<ThisProgram>().res_or_default::<Global>(); + r_println!("Name: {}, Age: {}", global.name, global.age) +} + +gen_program!(); diff --git a/mingling_core/src/asset.rs b/mingling_core/src/asset.rs index 234fec1..1ac37cb 100644 --- a/mingling_core/src/asset.rs +++ b/mingling_core/src/asset.rs @@ -12,6 +12,9 @@ pub mod dispatcher; pub mod enum_tag; #[doc(hidden)] +pub mod global_resource; + +#[doc(hidden)] pub mod node; #[doc(hidden)] diff --git a/mingling_core/src/asset/global_resource.rs b/mingling_core/src/asset/global_resource.rs new file mode 100644 index 0000000..5667576 --- /dev/null +++ b/mingling_core/src/asset/global_resource.rs @@ -0,0 +1,116 @@ +use std::{ + any::{Any, TypeId}, + collections::HashMap, + sync::{Arc, Mutex}, +}; + +use crate::{ChainProcess, Program, ProgramCollect}; + +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) { + if let Ok(mut guard) = self.resources.lock() { + guard.insert(TypeId::of::<Res>(), Box::new(Arc::new(res))); + } + } + + /// Modify a resource by type, applying a closure to the resource if present + pub fn modify_res<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::<Res>()) + .and_then(|a| a.downcast_mut::<Arc<Res>>()) + { + 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<Res: 'static + Send + Sync>(&self) -> Option<GlobalResource<Res>> { + 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))) + } + + /// Get a resource by type, returning `GlobalResource<Res>` if present + 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), + } + } + + /// Get a resource by type, returning `GlobalResource<Res>` or inserting a default + pub fn res_or_default<Res: 'static + Send + Sync + ResourceMarker>( + &self, + ) -> GlobalResource<Res> { + self.res() + .unwrap_or_else(|| GlobalResource::from(Arc::new(Res::res_default()))) + } +} + +/// Global assets for storing Program global state information +pub struct GlobalResource<ResType: 'static + Send + Sync> { + res_arc: Arc<ResType>, +} + +impl<ResType: 'static + Send + Sync> GlobalResource<ResType> { + /// Create a new `GlobalAsset` from an `AssetType` value. + pub fn new(res: ResType) -> Self { + Self { + res_arc: Arc::new(res), + } + } +} + +impl<ResType: 'static + Send + Sync> From<Arc<ResType>> for GlobalResource<ResType> { + fn from(arc: Arc<ResType>) -> Self { + Self { res_arc: arc } + } +} + +impl<ResType: 'static + Send + Sync> std::ops::Deref for GlobalResource<ResType> { + 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<T: Default + Clone> ResourceMarker for T { + fn res_clone(&self) -> Self { + Clone::clone(self) + } + + fn res_default() -> Self { + Default::default() + } +} diff --git a/mingling_core/src/lib.rs b/mingling_core/src/lib.rs index 9e65e40..b9fa816 100644 --- a/mingling_core/src/lib.rs +++ b/mingling_core/src/lib.rs @@ -24,6 +24,7 @@ pub use crate::asset::chain::*; pub use crate::asset::comp::*; pub use crate::asset::dispatcher::*; pub use crate::asset::enum_tag::*; +pub use crate::asset::global_resource::*; pub use crate::asset::help::*; pub use crate::asset::node::*; pub use crate::asset::renderer::*; diff --git a/mingling_core/src/program.rs b/mingling_core/src/program.rs index 949dd45..1505480 100644 --- a/mingling_core/src/program.rs +++ b/mingling_core/src/program.rs @@ -8,11 +8,15 @@ use crate::error::GeneralRendererSerializeError; use std::env; use crate::{ - AnyOutput, ChainProcess, RenderResult, + AnyOutput, ChainProcess, GlobalResources, RenderResult, asset::dispatcher::Dispatcher, error::{ChainProcessError, ProgramExecuteError}, }; -use std::{fmt::Display, sync::OnceLock}; +use std::{ + collections::HashMap, + fmt::Display, + sync::{Arc, Mutex, OnceLock}, +}; #[cfg(feature = "async")] use std::pin::Pin; @@ -66,6 +70,8 @@ where #[cfg(feature = "general_renderer")] pub general_renderer_name: GeneralRendererSetting, + + pub(crate) resources: GlobalResources, } impl<C> Program<C> @@ -101,6 +107,7 @@ where #[cfg(feature = "general_renderer")] general_renderer_name: GeneralRendererSetting::Disable, + resources: Arc::new(Mutex::new(HashMap::new())), } } diff --git a/mingling_macros/src/lib.rs b/mingling_macros/src/lib.rs index cce5eeb..8ba028b 100644 --- a/mingling_macros/src/lib.rs +++ b/mingling_macros/src/lib.rs @@ -152,6 +152,7 @@ pub fn gen_program(input: TokenStream) -> TokenStream { TokenStream::from(quote! { // Shit, this feature is unstable + // TODO :: This logic will be implemented when Rust's Impl In Type Alias feature becomes stable // pub type NextProcess = impl Into<::mingling::ChainProcess<#name>>; pub type NextProcess = ::mingling::ChainProcess<#name>; |
