diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-06-30 18:05:05 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-06-30 18:05:05 +0800 |
| commit | 13408e79b940e9a33ca593ed30d1b20c54e01234 (patch) | |
| tree | 282549991a3f31791401ca2f3255b9318679d2e9 /docs/pages/3-define-a-chain.md | |
| parent | 29867ab5c0b40378a33318d989c809f90fc7d3aa (diff) | |
feat(docs): add Chinese and English documentation for Mingling tutorials
Add comprehensive documentation covering Declare a Dispatcher, Declare a
Chain, Rendering Results, Multi-Command Program, Argument Parsing with
Picker and Clap, Program Setup, Error Handling, Help Info, Resource
System, Exit Code Control, Hook System, Testing, Completion, Structural
Rendering, and Core Concepts
Diffstat (limited to 'docs/pages/3-define-a-chain.md')
| -rw-r--r-- | docs/pages/3-define-a-chain.md | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/docs/pages/3-define-a-chain.md b/docs/pages/3-define-a-chain.md new file mode 100644 index 0000000..c6d3f8e --- /dev/null +++ b/docs/pages/3-define-a-chain.md @@ -0,0 +1,131 @@ +<h1 align="center">Declare a Chain</h1> +<p align="center"> + Use the <code>chain</code> macro to declare a chain and handle Entry input +</p> + +In the previous section, we declared `dispatcher!("greet", CMDGreet => EntryGreet)`. + +Now when a user types `greet`, it gets matched and wrapped into `EntryGreet`. + +But what happens after we get the Entry? + +We need a Chain to process it. + +## The `#[chain]` Macro + +`#[chain]` marks a handler function. The format is straightforward: + +```rust +@@@dispatcher!("greet", CMDGreet => EntryGreet); +pack!(ResultName = String); + +#[chain] +fn handle_greet(args: EntryGreet) -> Next { + // args contains the remaining params after matching user input + let name = args.inner.first().cloned().unwrap_or_else(|| "World".to_string()); + // Wrap the result into Next, telling the dispatcher where to go next + ResultName::new(name) +} +``` + +Notice anything? + +The Chain function signature declares what it needs — `args: EntryGreet`. + +Then it returns a newtype via `ResultName::new(name)`. + +This returned `Next` expands into `impl Into<ChainProcess<ThisProgram>>`. + +> [!TIP] +> Wondering how `Into<ChainProcess<G>>` works? +> +> Check out the [Any Output Mechanism](pages/concepts/3-any-output) chapter to learn about `ChainProcess`. + +## The `pack!` Macro + +You've probably guessed it — `pack!(ResultName = String)` defines a type that flows through the pipeline: + +```rust +// pack!(ResultName = String) generates code roughly like this + +#[derive(Groupped)] +pub struct ResultName { + pub inner: String, +} +``` + +Think of it as a **tagged** `String`. + +The dispatcher uses this tag for precise routing, ensuring data doesn't get mixed up — e.g., data sent to `RenderGreet` won't be misdelivered to `RenderError`. + +> [!NOTE] +> Unlike a simple type alias (`type`), `pack!` generates a completely new type with its own `TypeId`. + +Here's a recommended naming convention: + +| Role | Naming Pattern | Example | +| ------------ | ---------------------- | -------------------- | +| Entry | `Entry` + command | `EntryGreet` | +| Intermediate | `State` + description | `StateParsedArgs` | +| Result | `Result` + description | `ResultGreetSomeone` | +| Error | `Error` + description | `ErrorUserNotFound` | + +See [Naming Convention](pages/other/naming_rule) for details, but for now just remember: **use `pack!` to give your data a meaningful name**. + +## Extracting Params from Entry + +`EntryGreet`'s `inner` is a `Vec<String>`, which you can freely process inside a Chain: + +```rust +@@@dispatcher!("greet", CMDGreet => EntryGreet); +@@@pack!(ResultName = String); +#[chain] +fn handle_greet(args: EntryGreet) -> Next { + // Take the first param, or use a default + let name = args + .inner + .first() + .cloned() + .unwrap_or_else(|| "World".to_string()); + + ResultName::new(name) +} +``` + +If you enable the `parser` feature, you can also use `Picker` for more flexible param extraction — but that's a topic for later. + +## Putting It Together + +Now let's connect the Dispatcher and Chain: + +```rust +// 1. Declare the command +dispatcher!("greet", CMDGreet => EntryGreet); + +// 2. Declare the pipeline data type +pack!(ResultName = String); + +// 3. Processing logic +#[chain] +fn handle_greet(args: EntryGreet) -> Next { + let name = args.inner + .first() + .cloned() + .unwrap_or_else(|| "World".to_string()); + ResultName::new(name) +} + +fn main() { + let mut program = ThisProgram::new(); + program.with_dispatcher(CMDGreet); + program.exec_and_exit(); +} + +gen_program!(); +``` + +But this code isn't complete yet — we only have the Dispatcher and Chain. One last step remains: **rendering the result**. That's what the next chapter, Renderer, covers. + +<p align="center" style="font-size: 0.85em; color: gray;"> + Written by @Weicao-CatilGrass +</p> |
