From 13408e79b940e9a33ca593ed30d1b20c54e01234 Mon Sep 17 00:00:00 2001 From: 魏曹先生 <1992414357@qq.com> Date: Tue, 30 Jun 2026 18:05:05 +0800 Subject: 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 --- docs/pages/3-define-a-chain.md | 131 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 docs/pages/3-define-a-chain.md (limited to 'docs/pages/3-define-a-chain.md') 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 @@ +

Declare a Chain

+

+ Use the chain macro to declare a chain and handle Entry input +

+ +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>`. + +> [!TIP] +> Wondering how `Into>` 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`, 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. + +

+ Written by @Weicao-CatilGrass +

-- cgit