From a3b75b7eeae5e7a0c53b6167bafb75a85fa6d9ce Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Tue, 21 Jul 2026 03:58:44 +0800 Subject: feat(core): return `&mut Self` from builder methods Enables method chaining for `with_resource`, `with_dispatcher`, `with_dispatchers`, `with_hook`, and `with_setup` --- CHANGELOG.md | 26 ++++++++++++++++++++++++++ mingling_core/src/asset/dispatcher.rs | 6 ++++-- mingling_core/src/asset/global_resource.rs | 6 +++++- mingling_core/src/program/hook.rs | 3 ++- mingling_core/src/program/setup.rs | 3 ++- 5 files changed, 39 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6efe0e8..40e82d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -93,6 +93,32 @@ None Previously, when `render_output` was true but `result.is_empty()` was also true, the old code would skip printing entirely and return the exit code from the result. Now, `std_print()` is called unconditionally when `render_output` is true, and the exit code is read from the result regardless of whether any output was printed. This ensures that programs which set a non-zero exit code without producing renderable output (e.g., via `ExitCodeSetup` or `ProgramControlUnit::OverrideExitCode`) will exit with the correct code. +5. **[`core`]** Made `with_resource`, `with_dispatcher`, `with_dispatchers`, `with_hook`, and `with_setup` methods return `&mut Self` instead of `()`. These methods now return a mutable reference to the program instance, enabling ergonomic chaining: + + ```rust + // Before — required separate statements + program.with_resource(ResConfig::new()); + program.with_resource(ResDatabase::new()); + program.with_setup(BasicProgramSetup); + program.with_hook(logging_hook); + + // After — supports method chaining + program + .with_resource(ResConfig::new()) + .with_resource(ResDatabase::new()) + .with_setup(BasicProgramSetup) + .with_hook(logging_hook); + ``` + + Affected methods: + - `Program::with_resource(&mut self, res: Res) -> &mut Self` — Insert a resource into the program's global resource store. + - `Program::with_dispatcher(&mut self, dispatcher: Disp) -> &mut Self` — Add a single dispatcher to the program. + - `Program::with_dispatchers(&mut self, dispatchers: D) -> &mut Self` — Add multiple dispatchers to the program. + - `Program::with_hook(&mut self, hook: ProgramHook) -> &mut Self` — Add a lifecycle hook to the program. + - `Program::with_setup(&mut self, setup: S) -> &mut Self` — Load and execute a program setup. + + _No behavioral changes — all existing functionality is preserved. Downstream code that ignores the return value continues to work without modification._ + #### Features: 1. **[`core`]** Added `RenderResult::new()` method for creating a new `RenderResult` with default values (empty text and exit code 0). This provides a more explicit and discoverable constructor compared to `RenderResult::default()`, making it clearer when a fresh result is being created for use with `write!`/`writeln!`. 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(&mut self, dispatcher: Disp) + pub fn with_dispatcher(&mut self, dispatcher: Disp) -> &mut Self where Disp: Dispatcher + 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(&mut self, dispatchers: D) + pub fn with_dispatchers(&mut self, dispatchers: D) -> &mut Self where D: Into>, { @@ -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..a610378 100644 --- a/mingling_core/src/asset/global_resource.rs +++ b/mingling_core/src/asset/global_resource.rs @@ -13,10 +13,14 @@ where C: ProgramCollect, { /// Insert a resource of the given type, cloning the provided value into the store - pub fn with_resource(&mut self, res: Res) { + pub fn with_resource( + &mut self, + res: Res, + ) -> &mut Self { if let Ok(mut guard) = self.resources.lock() { guard.insert(TypeId::of::(), Box::new(Arc::new(res))); } + self } /// Modify a resource by type, applying a closure to the resource if present diff --git a/mingling_core/src/program/hook.rs b/mingling_core/src/program/hook.rs index db1691b..48f632f 100644 --- a/mingling_core/src/program/hook.rs +++ b/mingling_core/src/program/hook.rs @@ -144,8 +144,9 @@ where { /// Adds a typed hook to the program. The hook will be called at the appropriate /// lifecycle events. - pub fn with_hook(&mut self, hook: ProgramHook) { + pub fn with_hook(&mut self, hook: ProgramHook) -> &mut Self { self.hooks.push(hook); + self } pub(crate) fn run_hook_on_begin(&self, info: HookBeginInfo) { diff --git a/mingling_core/src/program/setup.rs b/mingling_core/src/program/setup.rs index 838c29a..a8ff114 100644 --- a/mingling_core/src/program/setup.rs +++ b/mingling_core/src/program/setup.rs @@ -29,8 +29,9 @@ where C: ProgramCollect, { /// Load and execute init logic - pub fn with_setup + 'static>(&mut self, setup: S) { + pub fn with_setup + 'static>(&mut self, setup: S) -> &mut Self { S::setup(setup, self); + self } } -- cgit