aboutsummaryrefslogtreecommitdiff
path: root/docs/pages/3-define-a-chain.md
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-08-17 05:49:19 +0800
committer魏曹先生 <1992414357@qq.com>2026-08-17 05:49:19 +0800
commit57c53affe3542cb6bd4e79ee4c18f20a1bd76b2d (patch)
tree1cd4aef44cb7a45a8cd9d520b598f5f181e24c76 /docs/pages/3-define-a-chain.md
parentef23cd944402939605c78a4a853ef6e33af02c21 (diff)
refactor!: replace pack! macros with derive-based pipeline types
Remove the `pack!`, `pack_err!`, `pack_structural!`, and `pack_err_structural!` macros, replacing all pipeline type definitions with `#[derive(Grouped)]` and `#[derive(Grouped, Wrap)]` attributes. This changes the generated struct shape from named-field structs with an `inner` field to tuple structs accessed via `.0`, and removes the auto-generated `name` and `info` fields from error types.
Diffstat (limited to 'docs/pages/3-define-a-chain.md')
-rw-r--r--docs/pages/3-define-a-chain.md54
1 files changed, 35 insertions, 19 deletions
diff --git a/docs/pages/3-define-a-chain.md b/docs/pages/3-define-a-chain.md
index 72ade94..ea39f6b 100644
--- a/docs/pages/3-define-a-chain.md
+++ b/docs/pages/3-define-a-chain.md
@@ -17,14 +17,15 @@ We need a Chain to process it.
```rust
@@@dispatcher!("greet", EntryGreet);
-pack!(ResultName = String);
+#[derive(Grouped, Wrap)]
+pub struct 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());
+ let name = args.0.first().cloned().unwrap_or_else(|| "World".to_string());
// Wrap the result into Next, telling the dispatcher where to go next
- ResultName::new(name).into()
+ ResultName(name).into()
}
```
@@ -32,7 +33,7 @@ Notice anything?
The Chain function signature declares what it needs — `args: EntryGreet`.
-Then it returns a newtype via `ResultName::new(name)`.
+Then it returns a newtype via `ResultName(name)`.
This returned `Next` expands into `impl Into<ChainProcess<ThisProgram>>`.
@@ -41,17 +42,30 @@ This returned `Next` expands into `impl Into<ChainProcess<ThisProgram>>`.
>
> Check out the [Any Output Mechanism](pages/concepts/3-any-output) chapter to learn about `ChainProcess`.
-## The `pack!` Macro
+## Declaring Types with `#[derive(Grouped, Wrap)]`
-You've probably guessed it — `pack!(ResultName = String)` defines a type that flows through the pipeline:
+You've probably guessed it — `#[derive(Grouped, Wrap)] pub struct ResultName(String);` defines a type that flows through the pipeline:
```rust
-// pack!(ResultName = String) generates code roughly like this
+// #[derive(Grouped, Wrap)] generates code roughly like this
-#[derive(Grouped)]
-pub struct ResultName {
- pub inner: String,
+pub struct ResultName(String);
+
+impl From<String> for ResultName {
+ fn from(inner: String) -> Self {
+ ResultName(inner)
+ }
+}
+
+impl std::ops::Deref for ResultName {
+ type Target = String;
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
}
+
+// Grouped generates member_id() → ThisProgram::ResultName,
+// giving the type its routing identity and Into<ChainProcess> conversion.
```
Think of it as a **tagged** `String`.
@@ -59,7 +73,7 @@ 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`.
+> Unlike a simple type alias (`type`), `#[derive(Grouped, Wrap)]` declares a completely new type with its own `TypeId`.
Here's a recommended naming convention:
@@ -70,25 +84,26 @@ Here's a recommended naming convention:
| 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**.
+See [Naming Convention](pages/other/naming_rule) for details, but for now just remember: **use `#[derive(Grouped)]` (optionally with `Wrap`) 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:
+`EntryGreet`'s `.0` is a `Vec<String>`, which you can freely process inside a Chain:
```rust
@@@dispatcher!("greet", EntryGreet);
-@@@pack!(ResultName = String);
+@@@#[derive(Grouped, Wrap)]
+@@@pub struct ResultName(String);
#[chain]
fn handle_greet(args: EntryGreet) -> Next {
// Take the first param, or use a default
let name = args
- .inner
+ .0
.first()
.cloned()
.unwrap_or_else(|| "World".to_string());
- ResultName::new(name).into()
+ ResultName(name).into()
}
```
@@ -103,16 +118,17 @@ Now let's connect the Dispatcher and Chain:
dispatcher!("greet", EntryGreet);
// 2. Declare the pipeline data type
-pack!(ResultName = String);
+#[derive(Grouped, Wrap)]
+pub struct ResultName(String);
// 3. Processing logic
#[chain]
fn handle_greet(args: EntryGreet) -> Next {
- let name = args.inner
+ let name = args.0
.first()
.cloned()
.unwrap_or_else(|| "World".to_string());
- ResultName::new(name).into()
+ ResultName(name).into()
}
fn main() {