diff options
Diffstat (limited to 'docs/pages/13-hook.md')
| -rw-r--r-- | docs/pages/13-hook.md | 107 |
1 files changed, 107 insertions, 0 deletions
diff --git a/docs/pages/13-hook.md b/docs/pages/13-hook.md new file mode 100644 index 0000000..0c5e460 --- /dev/null +++ b/docs/pages/13-hook.md @@ -0,0 +1,107 @@ +<h1 align="center">Hook System</h1> +<p align="center"> + How to insert custom behavior into a program using ProgramHook +</p> + +Hooks let you insert custom logic at various lifecycle points in the pipeline — before dispatch, after chain, before/after render, on program exit … + +You can write cross-cutting concerns (logging, auth, metrics collection) in hooks instead of scattering them across business code. + +## Basic Usage + +`ProgramHook` uses a builder pattern: + +```rust +@@@use mingling::hook::ProgramHook; +fn main() { + let mut program = ThisProgram::new(); + program.with_hook( + ProgramHook::empty() + .on_pre_chain(|info| { + println!("before chain: {}", info.input); + }) + .on_post_render(|info| { + println!("after render: {}", info.result); + }), + ); + program.exec_and_exit(); +} +``` + +> [!TIP] +> `ProgramHook::empty()` creates an empty hook, then chain-calls `.on_*()` methods to register the lifecycle nodes you care about. Unregistered nodes won't execute. + +## Lifecycle Nodes + +Hooks cover the full pipeline lifecycle: + +| Stage | Hook | Trigger Point | +| ------------ | ------------------ | ------------------- | +| **Dispatch** | `on_begin` | Execution start | +| | `on_pre_dispatch` | Before dispatch | +| | `on_post_dispatch` | After dispatch | +| **Chain** | `on_pre_chain` | Before chain exec | +| | `on_post_chain` | After chain exec | +| **Render** | `on_pre_render` | Before render exec | +| | `on_post_render` | After render exec | +| **Finish** | `on_finish` | Before program exit | + +Each hook callback receives a corresponding `Hook*Info` struct containing context info (input type, params, render results, etc.). + +## Real Example: Logging Operations + +```rust +@@@use mingling::prelude::*; +@@@use mingling::hook::ProgramHook; +@@@ +@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@pack!(ResultName = String); +@@@ +@@@#[chain] fn handle_greet(args: EntryGreet) -> Next { +@@@ ResultName::new(args.inner.first().cloned().unwrap_or_default()).to_render() +@@@} +@@@#[renderer] fn render_name(r: ResultName) { r_println!("Hello, {}!", *r); } +fn main() { + let mut program = ThisProgram::new(); + + // Log info before and after each chain execution + program.with_hook( + ProgramHook::empty() + .on_pre_chain(|info| { + eprintln!("[hook] executing chain for: {}", info.input); + }) + .on_post_chain(|info| { + eprintln!("[hook] chain output: {}", info.output.member_id); + }), + ); + + program.with_dispatcher(CMDGreet); + program.exec_and_exit(); +} +``` + +Run output: + +```text +[hook] executing chain for: EntryGreet +[hook] chain output: ResultName +Hello, World! +``` + +## Controlling Behavior via Hooks + +Hooks aren't just for observation — you can use `ProgramControlUnit` to alter program behavior: + +| Variant | Effect | +| -------------------------- | ----------------------------------------- | +| `Continue` | Do nothing, continue execution | +| `OverrideExitCode(i32)` | Override the exit code | +| `RouteToChain(AnyOutput)` | Replace current data, re-enter Chain loop | +| `RouteToRender(AnyOutput)` | Skip subsequent Chain, render directly | + +> [!NOTE] +> Multiple hooks can be registered and execute in registration order. + +<p align="center" style="font-size: 0.85em; color: gray;"> + Written by @Weicao-CatilGrass +</p> |
