aboutsummaryrefslogtreecommitdiff
path: root/docs/pages
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-04-28 22:39:59 +0800
committer魏曹先生 <1992414357@qq.com>2026-04-28 22:39:59 +0800
commit85ee549f68449bc70a7f1271a93ad26a8207ee40 (patch)
treebfb0b678d0f96c06b196417fd612a9cad2baf7fe /docs/pages
parent5bf4209bd138faf76e3bd316fdfa128a08f2bb2e (diff)
Rebuild and rewrite the documentation site infrastructure
Diffstat (limited to 'docs/pages')
-rw-r--r--docs/pages/.gitignore1
-rw-r--r--docs/pages/1-get-started.md278
-rw-r--r--docs/pages/1-get-started_zh_CN.md256
-rw-r--r--docs/pages/2-basic.md16
-rw-r--r--docs/pages/2-basic/1-program.md114
-rw-r--r--docs/pages/2-basic/2-setup.md150
-rw-r--r--docs/pages/2-basic/3-dispatcher.md95
-rw-r--r--docs/pages/2-basic/4-chain.md75
-rw-r--r--docs/pages/2-basic/5-renderer.md73
-rw-r--r--docs/pages/3-features.md15
-rw-r--r--docs/pages/3-features/1-parser.md376
-rw-r--r--docs/pages/3-features/2-general-renderer.md75
-rw-r--r--docs/pages/3-features/3-comp.md118
-rw-r--r--docs/pages/3-features/4-async.md6
-rw-r--r--docs/pages/3-features/5-clap-parser.md6
15 files changed, 491 insertions, 1163 deletions
diff --git a/docs/pages/.gitignore b/docs/pages/.gitignore
new file mode 100644
index 0000000..dd33554
--- /dev/null
+++ b/docs/pages/.gitignore
@@ -0,0 +1 @@
+.obsidian
diff --git a/docs/pages/1-get-started.md b/docs/pages/1-get-started.md
index d3358f4..1627ac4 100644
--- a/docs/pages/1-get-started.md
+++ b/docs/pages/1-get-started.md
@@ -1,66 +1,256 @@
-# Get Started
-This article explains how to quickly create your first **Mingling** command-line program.
+<h1 align="center">Get Started</h1>
+<p align="center">
+ Welcome to Mìng Lìng
+</p>
+
+## Intro
+
+ This chapter will guide you through **Mingling** step by step.
+
+ Before we start, let me explain what **Mingling** can do:
+
+ Without extra features, it is a sub-command dispatch system based on `proc-macro`: it matches user input, finds & creates the corresponding data, then pushes that data into a dispatcher that continually transforms its type. When the data can no longer be transformed, the program renders the final result to the terminal.
+
+ In other words, you need to understand a new dev paradigm: **a fully type-based dispatch system**. This may feel **frustrating** at first, but once you get the hang of it, you'll be able to write CLI apps that are super easy to modify and extend.
+
+
+
+## Creating a Basic Program
+
+ Next I'll walk you through creating a basic program—I assume you already have an empty Rust project ready!
+
+#### 1. Add Dependencies
+
+ Add the following deps to `Cargo.toml` ✏️
-## Quick Start
-1. Add `mingling` to your Rust project.
-```bash
-cargo add mingling
-```
-Or add the following to your `Cargo.toml`:
```toml
[dependencies]
-mingling = "0.1.6"
+mingling = "0.1.7"
+
+# If you want the latest, try the version hosted on Github
+mingling = { git = "https://github.com/catilgrass/mingling", branch = "main" }
+```
+
+> [!NOTE]
+>
+> This version matches the **Mingling** version used when writing this doc. Check [crates.io](https://crates.io/crates/mingling) for the latest release! 😄
+>
+> **Mingling** docs are actively updated to keep pace with the latest version.
+
+
+
+#### 2. Create the Program
+
+ Now, create the program in `src/main.rs` ✏️
+
+```rust
+fn main() {
+ // Create ThisProgram and run it
+ ThisProgram::new().exec();
+}
+
+// The gen_program! macro collects *all preceding* components & types
+// then generates the `ThisProgram` struct
+mingling::macros::gen_program!();
+```
+
+> [!TIP]
+>
+> When `gen_program!()` expands, it gathers info from other components & types that were expanded before it. This means you must place `gen_program!()` at the very last expansion point in the crate.
+>
+> I recommend putting it at the end of `main.rs` or `lib.rs`.
+
+
+
+#### 3. Create a Command
+
+ Of course, the program currently does nothing—it won't output anything at runtime. So let's create our first command `greet` and say hi to someone ✏️
+
+```rust
+fn main() {
+ // ...
+}
+
+// Create a dispatcher, binding GreetCommand to the "greet" sub-command
+// When the user specifies this command, send GreetEntry to the dispatcher
+dispatcher!("greet", GreetCommand => GreetEntry);
+
+// ...
+gen_program!();
```
-2. Write the basic code in your `main.rs` or other program entry point.
+ Don't be scared by the sudden macro and two new types! Let me explain what this macro does:
+
+##### About the `dispatcher!` macro 💡
+
+1. It creates a `GreetCommand` struct and implements the `Dispatcher` trait
+
+​ *This tells the framework: there's a new dispatcher that will handle a sub-command's behavior.*
+
+2. It implements the `Dispatcher` trait's `node(&self) -> Node` function, setting the node to `"greet"`
+
+​ *This tells the framework: this dispatcher handles the `"greet"` sub-command.*
+
+3. It implements the `Dispatcher` trait's `begin` function, converting the user's full input into the first type `GreetEntry`
+
+​ *This tells the framework: when this dispatcher is matched, it sends a `GreetEntry` type to the dispatcher for further processing.*
+
+ In short: **"When user types `greet`, I create a `GreetEntry` and throw it into the dispatcher for conversion."**
+
+
+
+#### 4. Register the Command
+
+ After creating the `Dispatcher`, we have two types: `GreetCommand` and `GreetEntry`. First, register `GreetCommand` with `ThisProgram` ✏️
+
```rust
-use mingling::macros::{dispatcher, gen_program, r_println, renderer};
-
fn main() {
- // Create ThisProgram
let mut program = ThisProgram::new();
-
- // Import the dispatcher `HelloCommand`
- program.with_dispatcher(HelloCommand);
-
- // Run the program
+
+ // Register the dispatcher
+ program.with_dispatcher(GreetCommand);
program.exec();
}
-
-// Define the dispatcher `HelloCommand`, which routes the "hello" subcommand to `HelloEntry`
-dispatcher!("hello", HelloCommand => HelloEntry);
-
-// Define the renderer, which receives `HelloEntry` and renders the content
+```
+
+ Now `ThisProgram` recognizes the `"greet"` sub-command, but the framework still doesn't know what `"greet"` should do. That's where we implement the actual logic:
+
+
+
+#### 5. Implement Rendering Behavior
+
+ We want `"greet"` to output `"Hello, World"`: since we're outputting to the screen, we can use another **Mingling** component, `Renderer`, which handles rendering data to the terminal ✏️
+
+```rust
+// ...
+dispatcher!("greet", GreetCommand => GreetEntry);
+
+// Declare a renderer `render_greet`, specifying the previous type as `GreetEntry`
#[renderer]
-fn render_hello(_prev: HelloEntry) {
- r_println!("Hello, World!")
+fn render_greet(_prev: GreetEntry) {
+ r_println!("Hello, World!");
}
-
-// Create ThisProgram at the end of the code
-gen_program!();
+
+// ...
+gen_program!(); // The renderer will be registered with the program
```
-3. Install your command-line program and run it.
-```bash
-cargo install --path ./
-your_bin hello
+ For functions marked with `#[renderer]`, **Mingling** strictly enforces only one function signature:
+
+```rust
+#[renderer]
+fn renderer_name (_prev: PreviousType) { }
```
-Result:
-```bash
-Hello, World!
+
+ The macro reads the type of the first param and tells `gen_program!` that this function renders that type.
+
+##### About `r_println!()` 💡
+
+ You might notice that the print macro used inside `#[renderer]` is `r_println!` instead of `println!`. This is because the framework's rendering logic doesn't happen inside that function: after `#[renderer]` expands, it injects a `r: &mut RenderResult` into the function; `r_println!` appends the message to the `RenderResult`, and after the dispatcher closes, the final rendered data is handed to `Program::exec` for output.
+
+
+
+#### 6. Add Execution Logic
+
+ I bet you're already itching to implement something like `greet Alice` to output `"Hello, Alice!"`—and this section is about to do just that!
+
+ **Mingling**'s core execution flow is `Dispatcher -> Chain -> Renderer`, and the key part is `Chain`: it converts the input data type into another type, then lets the dispatcher find the next `Chain` or `Renderer` based on the result type ✏️
+
+```rust
+dispatcher!("greet", GreetCommand => GreetEntry);
+
+// Wrap the intermediate type `ResultGreetSomeone`
+pack!(ResultGreetSomeone = String);
+
+#[chain]
+fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
+ let args = prev.inner;
+ let name = args
+ .first()
+ .cloned()
+ .unwrap_or_else(|| "World".to_string());
+
+ // Wrap into intermediate type
+ ResultGreetSomeone::new(name)
+}
+
+#[renderer]
+fn render_greet_someone(prev: ResultGreetSomeone) {
+ // Deref prev to get the raw type
+ r_println!("Hello, {}!", *prev);
+}
+```
+
+ Just like `#[renderer]`, we created a `#[chain]` that processes type `GreetEntry` and outputs `ResultGreetSomeone`.
+
+ This inserts a `Chain` between the original `Dispatcher` and `Renderer`: it extracts the user's input params (or falls back to "World"), then passes them to the renderer to print to the terminal.
+
+##### About `NextProcess` 💡
+
+ `NextProcess` is a placeholder generated by `gen_program!()`. After `#[chain]` expands, it's replaced by a type-erased type `ChainProcess<ThisProgram>` that the dispatcher can recognize, helping reduce boilerplate code.
+
+> [!NOTE]
+>
+> `NextProcess` is a temporary solution; the next update will wait until Rust's `Impl In Type Aliases` feature is stable.
+>
+> **But don't worry**: the next `NextProcess` update won't introduce **breaking changes!**
+
+##### About `pack!` 💡
+
+ `pack!` is an **extremely** frequently used macro in **Mingling** development: it wraps any type into another type and auto-derives the traits the framework needs.
+
+ Its syntax is as simple as you see:
+
+```rust
+pack!(PackedType = RawType);
```
-## About Async Runtime
+ Note: `pack!` doesn't support types with lifetimes, because types are always moved (not borrowed) between dispatchers.
+
+
+
+#### 7. Compile & Run
+
+ Alright, we've completed a basic CLI app. Here's the full code—you can paste it and run it directly:
+
+```rust
+use mingling::macros::{chain, dispatcher, gen_program, pack, r_println, renderer};
+
+fn main() {
+ let mut program = ThisProgram::new();
+ program.with_dispatcher(GreetCommand);
+ program.exec();
+}
+
+dispatcher!("greet", GreetCommand => GreetEntry);
-**Mingling** supports **async runtime**, you can enable the `async` feature to activate it.
+pack!(ResultGreetSomeone = String);
-After enabling it, **Mingling** will have the following changes:
+#[chain]
+fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
+ let args = prev.inner;
+ let name = args.first().cloned().unwrap_or_else(|| "World".to_string());
-- The `Chain` trait and `chain!` macro will require you to use **async functions**
-- `Program::exec` will become an async function
-- The `gen_program!` macro will generate async functions
+ ResultGreetSomeone::new(name)
+}
+
+#[renderer]
+fn render_greet_someone(prev: ResultGreetSomeone) {
+ r_println!("Hello, {}!", *prev);
+}
-**Mingling** does not depend on any specific asynchronous runtime internally, which means you can freely choose a suitable asynchronous runtime for your program (such as `async-std`, `tokio`)
+gen_program!();
+```
+
+ Output:
+
+```bash
+~> your-bin greet
+Hello, World!
+~> your-bin greet Alice
+Hello, Alice!
+```
-## 💡 Next Steps
-> **Mingling**'s basic components [Go](./pages/2-basic)
+<p align="center" style="font-size: 0.85em; color: gray;">
+ Written by @Weicao-CatilGrass
+</p>
diff --git a/docs/pages/1-get-started_zh_CN.md b/docs/pages/1-get-started_zh_CN.md
new file mode 100644
index 0000000..b72ad2e
--- /dev/null
+++ b/docs/pages/1-get-started_zh_CN.md
@@ -0,0 +1,256 @@
+<h1 align="center">Get Started</h1>
+<p align="center">
+ 欢迎使用 Mìng Lìng
+</p>
+
+## 前言
+
+ 本章节将介绍如何渐进式地了解 **Mingling**
+
+ 在开始之前,我先来讲讲 **Mingling** 能做什么:
+
+ 在未开启其他特性时,它本身是一个基于 `proc-macro` 的子命令调度系统:它匹配用户输入的文本,以此查找并创建具体的数据,并将该数据放入调度器中不断转换类型,当该数据被转换到无法转换时,程序会将最终的数据渲染到终端上。
+
+ 也就是说,您需要理解一套新的开发范式:**完全基于类型的调度系统**。这可能会让您前期的学习**充满挫败感**,但当您逐渐理解这套范式后,您将可以写出极其方便修改和拓展的命令行程序。
+
+
+
+## 创建基本程序
+
+ 接下来我将会讲述如何创建一个基本的程序,相信您已经准备好了一个空的 Rust 项目!
+
+#### 1. 添加依赖
+
+ 在 `Cargo.toml` 中添加如下依赖 ✏️
+
+```toml
+[dependencies]
+mingling = "0.1.7"
+
+# 如果您要尝鲜,可以试试 Github 上托管的版本
+mingling = { git = "https://github.com/catilgrass/mingling", branch = "main" }
+```
+
+> [!NOTE]
+>
+> 该版本基于文档编写时的 **Mingling** 版本,您可以前往 [crates.io](https://crates.io/crates/mingling) 查看最新的版本!😄
+>
+> **Mingling** 会积极更新文档,以确保文档内容能紧跟最新版本
+
+
+
+#### 2. 创建程序
+
+ 接下来,在 `src/main.rs` 中创建程序✏️
+
+```rust
+fn main() {
+ // 创建 ThisProgram,并执行
+ ThisProgram::new().exec();
+}
+
+// gen_program! 宏将会收集 *它之前* 的所有组件、类型
+// 然后生成程序 `ThisProgram`
+mingling::macros::gen_program!();
+```
+
+> [!TIP]
+>
+> `gen_Sprogram!()` 宏展开时,会收集在它之前展开的其他组件、类型的信息,这意味着您需要将 `gen_program!()` 放在整个 crate 中最后被展开的位置
+>
+> 我推荐放在 `main.rs` 或者 `lib.rs` 的结尾。
+
+
+
+#### 3. 创建命令
+
+ 当然,现在的程序什么都没有,在运行时不会输出任何消息。所以,让我们创建第一条命令 `greet`,给谁打个招呼吧 ✏️
+
+```rust
+fn main() {
+ // ...
+}
+
+// 创建分发器,并将 GreetCommand 绑定在 "greet" 子命令
+// 在用户指定该命令时,向调度器发送 GreetEntry
+dispatcher!("greet", GreetCommand => GreetEntry);
+
+// ...
+gen_program!();
+```
+
+ 不要被突然多出来的一个宏和两个类型所吓到!我来逐一解释这个宏干了什么:
+
+##### 关于 `dispatcher!` 宏 💡
+
+1. 宏创建了一个` GreetCommand` 结构体,并实现了 `Dispatcher` trait
+
+​ *这一步告诉框架:现在有了个新的分发器,它将会承接一个子命令的行为。*
+
+2. 宏实现了 `Dispatcher` trait 内部的 `node(&self) -> Node` 函数,并告诉节点为 `"greet"`
+
+​ *这一步告诉框架:该分发器将承接子命令 `"greet"` 的行为*
+
+3. 宏实现了 `Dispatcher` trait 内部的 `begin` 函数,将用户输入的完整参数转换为了第一个类型 `GreetEntry`
+
+​ *这一步告诉框架:该分发器在被匹配到后,将会向调度器发送类型 `GreetEntry`,供后续执行*
+
+ 简而言之:**“用户输入 `greet`,我就创建 `GreetEntry`,丢给调度器转换”**
+
+
+
+#### 4. 注册命令
+
+ 在 `Dispatcher` 创建后,我们得到了两个类型 `GreetCommand` 和 `GreetEntry`,首先将 `GreetCommand` 注册到 `ThisProgram` ✏️
+
+```rust
+fn main() {
+ let mut program = ThisProgram::new();
+
+ // 注册分发器
+ program.with_dispatcher(GreetCommand);
+ program.exec();
+}
+```
+
+ 这样,`ThisProgram` 就认得 `"greet"` 子命令了,但是框架还不知道 `"greet"` 的行为是怎样的。此时我们便需要实现具体的逻辑:
+
+
+
+#### 5. 实现渲染行为
+
+ 我们期望 `"greet"` 的时候输出 `"Hello, World"`:既然要输出到屏幕,那么我们可以使用 **Mingling** 的另一个组件 `Renderer`,它负责将数据渲染到终端 ✏️
+
+```rust
+// ...
+dispatcher!("greet", GreetCommand => GreetEntry);
+
+// 声明渲染器 `render_greet`,并表示前一个类型是 `GreetEntry`
+#[renderer]
+fn render_greet(_prev: GreetEntry) {
+ r_println!("Hello, World!");
+}
+
+// ...
+gen_program!(); // 渲染器会被注册到程序
+```
+
+ 对于 `#[renderer]` 属性宏标记的函数,**Mingling** 严格规定只允许使用一种函数签名:
+
+```rust
+#[renderer]
+fn renderer_name (_prev: PreviousType) { }
+```
+
+ 宏会读取到第一个参数的类型,并告诉 `gen_program!` 该函数用来渲染该类型。
+
+##### 关于 `r_println!()` 💡
+
+ 您可能会注意到,在 `#[renderer]` 中使用的打印宏是 `r_println!` 而非 `println!`,这是因为框架的渲染逻辑并不在该函数内:在 `#[renderer]` 展开后,会向函数注入一个 `r: &mut RenderResult`;而 `r_println!` 将信息追加到 `RenderResult` 内,并在调度器关闭后,将最终的渲染数据交给 `Program::exec` 函数输出。
+
+
+
+#### 6. 增加执行逻辑
+
+ 我猜您已经很想实现 `greet Alice` 这样的语法来输出 `"Hello, Alice!"` 了,本段正准备干这件事!
+
+ **Mingling** 的核心执行流程是 `Dispatcher -> Chain -> Renderer`,而最关键的就是 `Chain`:它负责将输入的数据类型转换为其他类型,然后让调度器根据结果的类型找到下一个 `Chain` 或者 `Renderer ✏️
+
+```rust
+dispatcher!("greet", GreetCommand => GreetEntry);
+
+// 包装中间类型 `ResultGreetSomeone`
+pack!(ResultGreetSomeone = String);
+
+#[chain]
+fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
+ let args = prev.inner;
+ let name = args
+ .first()
+ .cloned()
+ .unwrap_or_else(|| "World".to_string());
+
+ // 包装为中间类型
+ ResultGreetSomeone::new(name)
+}
+
+#[renderer]
+fn render_greet_someone(prev: ResultGreetSomeone) {
+ // 解引用 prev 拿到原始类型
+ r_println!("Hello, {}!", *prev);
+}
+```
+
+ 像 `#[renderer]` 一样,我们创建了一个 `#[chain]`,它处理类型 `GreetEntr`,输出 `ResultGreetSomeone`
+
+ 这样我们就在原本的 `Dispatcher` 和 `Renderer` 中间插入了一个 `Chain`:它可以将用户输入的参数提取出来(或回退到默认值 "World"),再交由渲染器打印到终端。
+
+##### 关于 `NextProcess` 💡
+
+ `NextProcess` 是由 `gen_program!()` 生成的占位符,在 `#[chain]` 展开后,它将被替换为调度器能识别的类型擦除类型 `ChainProcess<ThisProgram>`,用于减少代码量
+
+> [!NOTE]
+>
+> `NextProcess` 方案为临时替代,下一次更新需要等待 Rust 的 `Impl In Type Aliases` 特性稳定后。
+>
+> **不过,您不用担心**:下一次 `NextProcess` 的更新不会引入 **破坏性变更!**
+
+##### 关于 `pack!` 💡
+
+ `pack!` 是 **Mingling** 开发过程中使用频率 **极高** 的宏:它负责将任意类型包装成另一个类型,并自动为其派生框架所需的特征。
+
+ 它的语法如您所见,极为简单:
+
+```rust
+pack!(PackedType = RawType);
+```
+
+ 不过请注意:`pack!` 宏不支持带有生命周期的类型包装,因为类型在调度器之间的流转方式永远都是 `move` 而非 `borrow`。
+
+
+
+#### 7. 编译并运行
+
+ 好的,至此我们完成了一个基本的命令行程序,以下是完整代码,您可以直接粘贴运行:
+
+```rust
+use mingling::macros::{chain, dispatcher, gen_program, pack, r_println, renderer};
+
+fn main() {
+ let mut program = ThisProgram::new();
+ program.with_dispatcher(GreetCommand);
+ program.exec();
+}
+
+dispatcher!("greet", GreetCommand => GreetEntry);
+
+pack!(ResultGreetSomeone = String);
+
+#[chain]
+fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
+ let args = prev.inner;
+ let name = args.first().cloned().unwrap_or_else(|| "World".to_string());
+
+ ResultGreetSomeone::new(name)
+}
+
+#[renderer]
+fn render_greet_someone(prev: ResultGreetSomeone) {
+ r_println!("Hello, {}!", *prev);
+}
+
+gen_program!();
+```
+
+ 运行结果:
+
+```bash
+~> your-bin greet
+Hello, World!
+~> your-bin greet Alice
+Hello, Alice!
+```
+
+<p align="center" style="font-size: 0.85em; color: gray;">
+ Written by @Weicao-CatilGrass
+</p>
diff --git a/docs/pages/2-basic.md b/docs/pages/2-basic.md
deleted file mode 100644
index 78d2fda..0000000
--- a/docs/pages/2-basic.md
+++ /dev/null
@@ -1,16 +0,0 @@
-<h1 align="center">Mingling's Basic Components</h1>
-<p align="center">
- Table of Contents
-</p>
-
----
-
-Mingling abstracts the lifecycle of a CLI program into the following types:
-
-| Component | Description |
-| :--- | :--- |
-| [Program](./pages/2-basic/1-program) | Records resources for the current context |
-| [Setup](./pages/2-basic/2-setup) | Bundles commonly used functionalities |
-| [Dispatcher](./pages/2-basic/3-dispatcher) | Dispatches user input to specific types |
-| [Chain](./pages/2-basic/4-chain) | Handles type conversion |
-| [Renderer](./pages/2-basic/5-renderer) | Handles type rendering |
diff --git a/docs/pages/2-basic/1-program.md b/docs/pages/2-basic/1-program.md
deleted file mode 100644
index ae22896..0000000
--- a/docs/pages/2-basic/1-program.md
+++ /dev/null
@@ -1,114 +0,0 @@
-<h1 align="center">Program</h1>
-<p align="center">
- Mingling's Basic Components
-</p>
-
----
-
-## Intro
-
-`Program` is the data structure that holds the state for **Mingling** CLI programs. It manages the user's context and enables type-based dispatch.
-
-`Program` needs to implement the `ProgramCollect` trait,
-
-> but, you don't have to do this manually —
-
-The `mingling_macros` crate provides the `gen_program!()` macro, which can auto collect resources defined by the `dispatcher!`, `chain!`, and the `completion!` macro of the `comp` feature.
-
-```rust
-// Define Dispatcher
-dispatcher!("hello", HelloCommand => HelloEntry);
-
-// Define Renderer
-#[renderer]
-fn render_hello(_prev: HelloEntry) {
- r_println!("Hello, World!")
-}
-
-// Collect all resources here and generate ThisProgram
-gen_program!();
-
-// You can also explicitly declare a Program
-// with a different name like this:
-// gen_program!(MyProgram);
-```
-
-## Adding Setup
-
-You can use the `with_setup` function to add preset [Setup](pages/2-basic/2-setup) to your program, which provide reusable functionality.
-
-For example, you can use the following code to add parsing for global flags like `--confirm` / `--help` / `--quiet` to your program:
-
-```rust
-use mingling::{
- macros::gen_program,
- setup::BasicProgramSetup
-};
-
-fn main() {
- let mut program = ThisProgram::new();
- // Add `BasicProgramSetup`
- program.with_setup(BasicProgramSetup);
- program.exec();
-}
-
-// Generate `ThisProgram`
-gen_program!();
-```
-
-## Adding Dispatcher
-
-You can use `with_dispatcher` or `with_dispatchers` to add [Dispatchers](pages/2-basic/3-dispatcher) to your program to make it work:
-
-```rust
-// Define two Dispatchers using `dispatcher!`
-dispatcher!("member.add",
- AddMemberCommand => AddMemberEntry);
-dispatcher!("member.rm",
- RemoveMemberCommand => RemoveMemberEntry);
-
-fn main() {
- let mut program = ThisProgram::new();
-
- // Register Dispatchers
- program.with_dispatcher(AddMemberCommand);
- program.with_dispatcher(RemoveMemberCommand);
-
- // Or use `with_dispatchers`
- program.with_dispatchers((
- AddMemberCommand,
- RemoveMemberCommand
- ));
-
- program.exec();
-}
-```
-
-## Parsing Global Args
-
-You can extract global arguments before the program runs to control the global state of the `Program`:
-
-```rust
-fn main() {
- let mut program = ThisProgram::new();
-
- let mut output = current_dir().unwrap();
-
- // Pick the "--quiet" or "-q" flag
- program.global_flag(["--quiet", "-q"], |p| {
- // Disable render output
- p.stdout_setting.render_output = false;
- });
-
- // Pick the "--output" or "-O" flag, write to output
- program.global_argument(
- ["--output", "-O"],
- |_, v| output = PathBuf::from(v)
- );
-
- program.exec();
-}
-```
-
-## 💡 Next Page
-> **Basic Component** - Setup [Go](./pages/2-basic/2-setup)
diff --git a/docs/pages/2-basic/2-setup.md b/docs/pages/2-basic/2-setup.md
deleted file mode 100644
index 102e213..0000000
--- a/docs/pages/2-basic/2-setup.md
+++ /dev/null
@@ -1,150 +0,0 @@
-<h1 align="center">Setup</h1>
-<p align="center">
- Mingling's Basic Components
-</p>
-
----
-
-## Intro
-
-`Setup` is used to organize and package the initialization process of a `Program`, making the project easier to manage.
-
-## Usage
-
-It is defined as follows:
-
-```rust
-struct MySetup;
-impl ProgramSetup<ThisProgram>
- for MySetup
-{
- fn setup(
- &mut self,
- program: &mut Program<ThisProgram>
- ) {
- // Your setup logic
- }
-}
-```
-
-For example:
-
-```rust
-use std::{env::current_dir, path::PathBuf};
-
-use mingling::{
- Program,
- macros::{dispatcher, gen_program, renderer},
- setup::ProgramSetup,
-};
-
-// Global state
-static OUTPUT_PATH: std::sync::OnceLock<PathBuf>
- = std::sync::OnceLock::new();
-
-fn main() {
- let mut program = ThisProgram::new();
- program.with_setup(MySetup);
- program.exec();
-}
-
-// Define two Dispatchers using `dispatcher!`
-dispatcher!("member.add",
- AddMemberCommand => AddMemberEntry);
-dispatcher!("member.rm",
- RemoveMemberCommand => RemoveMemberEntry);
-
-struct MySetup;
-impl ProgramSetup<ThisProgram, ThisProgram> for MySetup {
- fn setup(
- &mut self, program: &mut Program<ThisProgram, ThisProgram>
- ) {
- // Register Dispatchers
- program.with_dispatcher(AddMemberCommand);
- program.with_dispatcher(RemoveMemberCommand);
-
- // Initialize global output once
- OUTPUT_PATH.get_or_init(|| current_dir().unwrap());
-
- // Pick the "--quiet" or "-q" flag
- program.global_flag(["--quiet", "-q"], |p| {
- // Disable render output
- p.stdout_setting.render_output = false;
- });
-
- // Pick the "--output" or "-O" flag, write to output
- program.global_argument(["--output", "-O"], |_, v| {
- let _ = OUTPUT_PATH.set(PathBuf::from(v));
- });
- }
-}
-
-gen_program!();
-```
-
-## Simplified Syntax
-
-If you find the above declaration method too **verbose**, you can use the `program_setup!` macro to simplify it. The format is:
-
-```rust
-#[program_setup]
-fn my_setup(
- program: &mut Program<ThisProgram>
-) {
- // Your setup logic
-}
-```
-
-For example:
-
-```rust
-use std::{env::current_dir, path::PathBuf};
-
-use mingling::{
- Program,
- macros::{
- dispatcher,
- gen_program,
- program_setup,
- renderer
- },
-};
-
-static OUTPUT_PATH: std::sync::OnceLock<PathBuf>
- = std::sync::OnceLock::new();
-
-#[tokio::main]
-async fn main() {
- let mut program = ThisProgram::new();
- program.with_setup(MySetup);
- program.exec().await;
-}
-
-dispatcher!("member.add",
- AddMemberCommand => AddMemberEntry);
-dispatcher!("member.rm",
- RemoveMemberCommand => RemoveMemberEntry);
-
-#[program_setup]
-fn my_setup(
- program: &mut Program<ThisProgram>
-) {
- program.with_dispatcher(AddMemberCommand);
- program.with_dispatcher(RemoveMemberCommand);
-
- OUTPUT_PATH.get_or_init(|| current_dir().unwrap());
-
- program.global_flag(["--quiet", "-q"], |p| {
- p.stdout_setting.render_output = false;
- });
-
- program.global_argument(["--output", "-O"], |_, v| {
- let _ = OUTPUT_PATH.set(PathBuf::from(v));
- });
-}
-
-gen_program!();
-```
-
-## 💡 Next Page
-> **Basic Component** - Dispatcher [Go](./pages/2-basic/3-dispatcher)
diff --git a/docs/pages/2-basic/3-dispatcher.md b/docs/pages/2-basic/3-dispatcher.md
deleted file mode 100644
index 66e5c90..0000000
--- a/docs/pages/2-basic/3-dispatcher.md
+++ /dev/null
@@ -1,95 +0,0 @@
-<h1 align="center">Dispatcher</h1>
-<p align="center">
- Mingling's Basic Components
-</p>
-
----
-
-## Intro
-
-`Dispatcher` is a core concept in **Mingling**, used to dispatch user-input arguments to corresponding types, which are then handled by [Chain](pages/2-basic/4-chain) or [Renderer](pages/2-basic/5-renderer).
-
-To define a `Dispatcher`, it is recommended to use the `dispatcher!` macro provided by `mingling_macros`:
-
-```rust
-// User input: your_bin hello
-// Will access HelloCommand and
-// dispatch arguments to HelloEntry
-dispatcher!("hello",
- HelloCommand => HelloEntry);
-
-// User input: your_bin sub foo
-// Will access FooCommand and
-// dispatch arguments to FooEntry
-dispatcher!("sub.foo",
- FooCommand => FooEntry);
-
-// Same as above
-dispatcher!("sub.bar",
- BarCommand => BarEntry);
-```
-
-If you explicitly specify a name in the `gen_program!` macro, for example:
-
-```rust
-gen_program!(MyProgram);
-```
-
-Then when using the `dispatcher!` macro, you must also explicitly specify the [Program](pages/2-basic/1-program):
-
-```rust
-dispatcher!(MyProgram, "hello",
- HelloCommand => HelloEntry);
-```
-
-**Tips:** Finally, add the `Dispatcher` you created to the [Program](pages/2-basic/1-program):
-
-```rust
-fn main() {
- let mut program = ThisProgram::new();
- program.with_dispatcher(HelloCommand);
- program.with_dispatcher(SubFooCommand);
- program.with_dispatcher(SubBarCommand);
- program.exec();
-}
-```
-
-## Manual Impl
-
-You can also manually implement the basic `Dispatcher` for more fine-grained control. However, compared to the procmacro, it is more cumbersome and cannot intelligently introduce certain traits based on the state of feature flags.
-
-```rust
-// Define AddMemberEntry
-// Use the `Groupped` derive to
-// mark AddMemberEntry as a member of ThisProgram
-#[derive(Debug, Groupped)]
-pub struct AddMemberEntry {
- // Define arguments to store user input
- pub(crate) args: Vec<String>,
-}
-
-// Implement the Dispatcher trait
-impl Dispatcher<ThisProgram> for AddMemberCommand {
- // Return the node name of this Dispatcher
- fn node(&self) -> Node {
- node!("member.add")
- }
-
- // When executing this Dispatcher, output AddMemberEntry
- fn begin(&self, args: Vec<String>)
- -> ChainProcess<ThisProgram>
- {
- AnyOutput::new(AddMemberEntry { args }).route_chain()
- }
-
- // Used to implement the clone trait for this Dispatcher
- fn clone_dispatcher(&self)
- -> Box<dyn Dispatcher<ThisProgram>>
- {
- Box::new(AddMemberCommand)
- }
-}
-```
-
-## 💡 Next Page
-> **Basic Component** - Chain [Go](./pages/2-basic/4-chain)
diff --git a/docs/pages/2-basic/4-chain.md b/docs/pages/2-basic/4-chain.md
deleted file mode 100644
index d47642a..0000000
--- a/docs/pages/2-basic/4-chain.md
+++ /dev/null
@@ -1,75 +0,0 @@
-<h1 align="center">Chain</h1>
-<p align="center">
- Mingling's Basic Components
-</p>
-
----
-
-## Intro
-
-Like `Dispatcher`, `Chain` is also a core concept in building the entire **Mingling** framework. It is used to receive a dispatch of one type and convert it into another type.
-
-```rust
-dispatcher!("hello",
- HelloCommand => HelloEntry);
-
-// Define intermediate type ParsedHello, internally a String
-pack!(ParsedHello = String);
-
-// Define chain parse_hello (expands to ParseHello)
-// Declare conversion from HelloEntry
-#[chain]
-fn parse_hello(prev: HelloEntry) -> NextProcess {
- // Take the inner reference of HelloEntry
- let args = &*prev;
-
- // Extract the first argument, use default value "World"
- // if it doesn't exist
- let first = args.first().cloned().unwrap_or_else(|| "World".to_string());
-
- // Pack the extracted argument into ParsedHello and
- // dispatch to the next chain
- ParsedHello::new(first).to_chain()
-}
-```
-
-> [!Tip]
-> `NextProcess` is a marker type in **Mingling**, from `mingling::marker`.
->
-> It serves no functional purpose other than to simplify the declaration of chain functions. After the `chain!` macro expands, `NextProcess` will be replaced with `mingling::ChainProcess<ThisProgram>`.
-
-## Manual Impl
-
-You can also manually implement the basic `Chain` for finer control.
-
-However, please note that within the `chain!` macro, a `register_type!` macro is executed. This macro does not expand to any content; it only informs the `gen_program` context that this type exists.
-
-```rust
-dispatcher!("hello",
- HelloCommand => HelloEntry);
-
-pack!(ParsedHello = String);
-
-struct ParseHello;
-impl Chain<ThisProgram> for ParseHello {
- type Previous = HelloEntry;
- fn proc(prev: Self::Previous)
- -> ChainProcess<ThisProgram>
- {
- let args = &*prev;
- let first = args
- .first()
- .cloned()
- .unwrap_or_else(||
- "World".to_string()
- );
- ParsedHello::new(first).to_chain()
- }
-}
-
-// Register chain to the context
-register_chain!(HelloEntry, ParseHello);
-```
-
-## 💡 Next Page
-> **Basic Component** - Renderer [Go](./pages/2-basic/5-renderer)
diff --git a/docs/pages/2-basic/5-renderer.md b/docs/pages/2-basic/5-renderer.md
deleted file mode 100644
index 2085d40..0000000
--- a/docs/pages/2-basic/5-renderer.md
+++ /dev/null
@@ -1,73 +0,0 @@
-<h1 align="center">Renderer</h1>
-<p align="center">
- Mingling's Basic Components
-</p>
-
----
-
-## Intro
-
-`Renderer` is similar to [Chain](pages/2-basic/4-chain) in that they both handle processing for a specific type. The difference is: [Chain](pages/2-basic/4-chain) transforms the type, while `Renderer` terminates the program and prints the information of that type to the terminal.
-
-A type can be processed by both [Chain](pages/2-basic/4-chain) and `Renderer`. If the type is `route_chain`ed, the system will search for a [Chain](pages/2-basic/4-chain) capable of handling that type. If none is found, it will automatically be routed to the `Renderer` to print the result of that type.
-
-The following example demonstrates how to handle rendering logic:
-
-```rust
-dispatcher!("hello",
- HelloCommand => HelloEntry);
-
-pack!(ParsedHello = String);
-
-// It's the Chain defined in the Dispatcher chapter
-#[chain]
-fn parse_hello(prev: HelloEntry) -> NextProcess {
- let args = &*prev;
- let first = args
- .first()
- .cloned()
- .unwrap_or_else(||
- "World".to_string()
- );
-
- // Distribute the type to the Renderer
- ParsedHello::new(first).to_render()
-}
-
-// Define the renderer to
-// handle rendering of ParsedHello
-#[renderer]
-fn render_hello(prev: ParsedHello) {
- // Use r_println or r_print to
- // render the content of ParsedHello
- r_println!("Hello, {}!", *prev)
-}
-```
-
-> [!Tip]
-> `r_print!` can only be used inside a `Renderer`. This is because after the `renderer!` macro expands, it injects `r: &mut RenderResult` into the context.
->
-> And `r_print!` directly writes content to the value `r`.
-> This means: if there is no `&mut RenderResult` named `r` in the context, `r_print!` cannot be used.
-
-## Manual Impl
-
-Similarly, you can also manually implement `Renderer`,
-
-but note that inside the `renderer!` macro, a `register_type!` macro is executed. This macro itself does not expand into any content; it is only used to inform the `gen_program` context that the type exists:
-
-```rust
-struct RenderHello;
-impl Renderer for RenderHello {
- type Previous = ParsedHello;
- fn render(
- prev: Self::Previous,
- r: &mut RenderResult
- ) {
- r_println!("Hello, {}!", *prev)
- }
-}
-
-// Register renderer to the context
-register_renderer!(ParsedHello, RenderHello);
-```
diff --git a/docs/pages/3-features.md b/docs/pages/3-features.md
deleted file mode 100644
index a2eac63..0000000
--- a/docs/pages/3-features.md
+++ /dev/null
@@ -1,15 +0,0 @@
-<h1 align="center">Mingling's Features</h1>
-<p align="center">
- Table of Contents
-</p>
-
----
-
-Mingling provides the following features for use:
-
-| Feature | Description |
-| :--- | :--- |
-| [Parser](./pages/3-features/1-parser) | Converts user input into structured data |
-| [General Renderer](./pages/3-features/2-general-renderer) | Enables the `Renderer` to support serialization into different output formats |
-| [Completion](./pages/3-features/3-comp) | Enables the program to support dynamic command-line completion |
-| [Async](./pages/3-features/4-async) | Provides async execution capabilities |
diff --git a/docs/pages/3-features/1-parser.md b/docs/pages/3-features/1-parser.md
deleted file mode 100644
index 0b5ded5..0000000
--- a/docs/pages/3-features/1-parser.md
+++ /dev/null
@@ -1,376 +0,0 @@
-<h1 align="center">Parser</h1>
-<p align="center">
- Mingling's Features
-</p>
-
----
-
-## Enable Feature
-
-`parser` is a feature provided by **Mingling**. You can enable it in the following way:
-
-```toml
-[dependencies]
-mingling = {
- version = "...",
- features = ["parser"]
-}
-```
-
-## Usage
-
-`parser` provides the ability to transform user input into structured data. Its core concept is **pick**.
-
-The following demonstrates the parsing approach without using a `Picker`:
-
-```rust
-#[chain]
-fn parse_hello(prev: HelloEntry) -> NextProcess {
- let args = &*prev;
- let first = args.first().cloned().unwrap_or_else(|| "World".to_string());
- ParsedHello::new(first).to_render()
-}
-```
-
-This is how it looks when using `Picker`:
-
-```rust
-#[chain]
-fn parse_hello(prev: HelloEntry) -> NextProcess {
- // Create Picker
- let picker = Picker::<ThisProgram>::new(prev.inner);
-
- // Extract the first argument from the Picker,
- // fallback to "World" if it doesn't exist
- let first = picker
- .pick_or((), "World")
- .unpack_directly();
-
- ParsedHello::new(first).to_render()
-}
-```
-
-You might notice that using `Picker` can sometimes make statements more verbose, but this is only when parsing a small number of arguments. What if we complicate the scenario?
-
-Suppose we want to design the following commands:
-
-```bash
-# Eat 1 apple weighing at least 20
-fruit eat Apple --min-weight 20
-
-# Eat 10 apples weighing at least 20
-fruit eat Apple --min-weight 20 --count 10
-
-# Eat 1 apple weighing between 10 and 20
-fruit eat Apple --min-weight 10 --max-weight 20
-
-# Eat 1 apple weighing between 20 and 10 (incorrect logic)
-fruit eat Apple --min-weight 20 --max-weight 10
-
-# When no specific fruit is specified, eat banana
-fruit eat --count 5
-```
-
-For this complex scenario, the `Picker` comes into play!
-
-We first design the type `ParsedEatFruit`
-
-```rust
-#[derive(Debug, Default, Groupped)]
-struct ParsedEatFruit {
- count: i16,
- weight_range: (i16, i16),
- fruit_type: Fruit,
-}
-
-#[derive(Debug, Default, EnumTag)]
-enum Fruit {
- #[default]
- Banana,
- Apple,
- Orange,
-}
-```
-
-Then create the basic binary program `fruit`
-
-```rust
-use mingling::{
- EnumTag, Groupped,
- macros::{chain, dispatcher, gen_program, r_println, renderer},
- parser::PickableEnum,
-};
-
-fn main() {
- let mut program = ThisProgram::new();
- program.with_dispatcher(FruitEatCommand);
- program.exec();
-}
-
-dispatcher!("eat",
- FruitEatCommand => FruitEatEntry);
-
-#[derive(Debug, Default, Groupped)]
-struct ParsedEatFruit {
- count: i16,
- weight_range: (i16, i16),
- fruit_type: Fruit,
-}
-
-#[derive(Debug, Default, EnumTag)]
-enum Fruit {
- #[default]
- Banana,
- Apple,
- Orange,
-}
-
-// Implement PickableEnum for Fruit to make it pickable
-impl PickableEnum for Fruit {}
-
-#[chain]
-fn parse_fruit_eat(prev: FruitEatEntry) -> NextProcess {
- // ...
-}
-
-#[renderer]
-fn render_fruit_eat(prev: ParsedEatFruit) {
- let weight_str = match prev.weight_range {
- (min, max) if min == 0 && max > 0 => {
- format!("up to {}.", max)
- }
- (min, max) if min > 0 && max == 0 => {
- format!("at least {}.", min)
- }
- (min, max) if min > 0 && max > 0 && min != max => {
- format!("between {} and {}.", min, max)
- }
- (min, max) if min > 0 && max > 0 && min == max => {
- format!("exactly {}.", min)
- }
- _ => "unknown.".to_string(),
- };
-
- let fruit_type = if prev.count > 1 {
- format!("{}s", prev.fruit_type.enum_info().0)
- } else {
- prev.fruit_type.enum_info().0.to_string()
- };
-
- r_println!(
- "I ate {} {}, each weighing {}",
- prev.count,
- fruit_type,
- weight_str
- );
-}
-
-gen_program!();
-```
-
-Now focus on writing the logic for `parse_fruit_eat`:
-
-> Review the business logic:
->
-> 1 - The default fruit is Banana
->
-> 2 - The default quantity is 1
->
-> 3 - The default weight is (0, 0)
->
-> 4 - When `max-weight` is less than `min-weight`, the business logic is in error
-
-Before writing the code, define the error type `MinGreaterThanMax` and the related `Renderer`
-
-```rust
-pack!(MinGreaterThanMax = ());
-
-#[renderer]
-fn render_min_greater_than_max(_prev: MinGreaterThanMax) {
- r_println!("Error: min weight cannot be greater than max weight.");
-}
-```
-
-Now start writing the logic:
-
-```rust
-#[chain]
-fn parse_fruit_eat(prev: FruitEatEntry) -> NextProcess {
- let picker = Picker::new(prev.inner);
- let mut min_weight: i16 = 0;
- let parsed = picker
- .pick_or(["--count", "-n"], 1)
- .pick::<i16>("--min-weight") // default: 0
- .after(|min| {
- // Copy `min` to external variable
- min_weight = min;
- min
- })
- .pick_or::<i16>("--max-weight", min_weight) // default: min_weight
- .after_or_route(|max| {
- // Check if `max` is valid
- if max < &min_weight {
- Err(MinGreaterThanMax::default())
- } else {
- Ok(max.clone())
- }
- })
- .pick(())
- // Since there's a possibility of being routed,
- // don't use `unpack_directly`
- .unpack();
-
- match parsed {
- Ok((count, min_weight, max_weight, fruit_type)) => {
- let parsed = ParsedEatFruit {
- count,
- weight_range: (min_weight, max_weight),
- fruit_type,
- };
-
- AnyOutput::new(parsed).route_renderer()
- }
- Err(route) => route.to_render(),
- }
-}
-```
-
-Complete code:
-
-```rust
-use mingling::{
- AnyOutput, EnumTag, Groupped,
- macros::{chain, dispatcher, gen_program, pack, r_println, renderer},
- parser::{PickableEnum, Picker},
-};
-
-fn main() {
- let mut program = ThisProgram::new();
- program.with_dispatcher(FruitEatCommand);
- program.exec();
-}
-
-dispatcher!("eat",
- FruitEatCommand => FruitEatEntry);
-
-#[derive(Debug, Default, Groupped)]
-struct ParsedEatFruit {
- count: i16,
- weight_range: (i16, i16),
- fruit_type: Fruit,
-}
-
-#[derive(Debug, Default, EnumTag)]
-enum Fruit {
- #[default]
- Banana,
- Apple,
- Orange,
-}
-
-impl PickableEnum for Fruit {}
-
-pack!(MinGreaterThanMax = ());
-
-#[chain]
-fn parse_fruit_eat(prev: FruitEatEntry) -> NextProcess {
- let picker = Picker::new(prev.inner);
- let mut min_weight: i16 = 0;
- let (count, min_weight, max_weight, fruit_type) = route! {
- picker
- // Pick count
- .pick_or::<i16>(["--count", "-n"], 1 as i16)
-
- // Pick min/max weight
- .pick::<i16>("--min-weight")
- .after(|min| {
- min_weight = min;
- min
- })
-
- .pick_or::<i16>("--max-weight", min_weight)
- .after_or_route(|max| {
- if max < &min_weight {
- Err(MinGreaterThanMax::default().to_render())
- } else {
- Ok(max.clone())
- }
- })
-
- // Pick Type
- .pick(())
- .unpack()
- };
-
- ParsedEatFruit {
- count,
- weight_range: (min_weight, max_weight),
- fruit_type,
- }
- .to_render()
-}
-
-#[renderer]
-fn render_min_greater_than_max(_prev: MinGreaterThanMax) {
- r_println!("Error: min weight cannot be greater than max weight.");
-}
-
-#[renderer]
-fn render_fruit_eat(prev: ParsedEatFruit) {
- let weight_str = match prev.weight_range {
- (min, max) if min == 0 && max > 0 => {
- format!("up to {}.", max)
- }
- (min, max) if min > 0 && max == 0 => {
- format!("at least {}.", min)
- }
- (min, max) if min > 0 && max > 0 && min != max => {
- format!("between {} and {}.", min, max)
- }
- (min, max) if min > 0 && max > 0 && min == max => {
- format!("exactly {}.", min)
- }
- _ => "unknown.".to_string(),
- };
-
- let fruit_type = if prev.count > 1 {
- format!("{}s", prev.fruit_type.enum_info().0)
- } else {
- prev.fruit_type.enum_info().0.to_string()
- };
-
- r_println!(
- "I ate {} {}, each weighing {}",
- prev.count,
- fruit_type,
- weight_str
- );
-}
-
-gen_program!();
-```
-
-Now compile the program and run it:
-
-```bash
-cargo install --path ./
-```
-
-Running results:
-
-```bash
-~> fruit eat Apple --min-weight 20
-I ate 1 Apple, each weighing exactly 20.
-
-~> fruit eat Apple --min-weight 20 --count 10
-I ate 10 Apples, each weighing exactly 20.
-
-~> fruit eat Apple --min-weight 10 --max-weight 20
-I ate 1 Apple, each weighing between 10 and 20.
-
-~> fruit eat Apple --min-weight 20 --max-weight 10
-Error: min weight cannot be greater than max weight.
-
-~> fruit eat --count 5
-I ate 5 Bananas, each weighing unknown.
-```
diff --git a/docs/pages/3-features/2-general-renderer.md b/docs/pages/3-features/2-general-renderer.md
deleted file mode 100644
index c3b81a2..0000000
--- a/docs/pages/3-features/2-general-renderer.md
+++ /dev/null
@@ -1,75 +0,0 @@
-<h1 align="center">General Renderer</h1>
-<p align="center">
- Mingling's Features
-</p>
-
----
-
-## Enable Feature
-
-`general_renderer` is a feature provided by **Mingling**. You can enable it in the following way:
-
-```toml
-[dependencies]
-mingling = {
- version = "...",
- features = ["general_renderer"]
-}
-```
-
-## Setup
-
-`general_renderer` requires you to implement the `serde::Serialize` trait for **all** structs, so your project needs to include `serde`
-
-```toml
-[dependencies]
-serde = {
- version = "1",
- features = ["derive"]
-}
-```
-
-For types wrapped with the `pack!` macro, `serde::Serialize` will be automatically implemented
-
-```rust
-pack!(YourInfo = ()); // Auto derive `serde::Serialize`
-```
-
-For types using the derive macro `Groupped`, you need to manually implement `serde::Serialize`
-
-```rust
-#[derive(Default, Groupped, Serialize)]
-struct YourInfo {
- name: String,
- age: i32,
-}
-```
-
-> [!Tip]
-> If there are types that do not implement `serde::Serialize`, compilation will fail.
-
-## Import GeneralRendererSetup
-
-`general_renderer` provides a Setup type called `GeneralRendererSetup`.
-
-After importing it into your program,
-user inputs like `--json`, `--yaml`, `--toml`, `--ron`, `--json-pretty`, and `--ron-pretty` will be automatically recognized.
-
-During the **rendering phase**, instead of the **default renderer**, the serialized content will be displayed to the terminal.
-
-```rust
-fn main() {
- let mut program = ThisProgram::new();
-
- // Add General Renderer
- program.with_setup(GeneralRendererSetup);
-
- // Add Dispatchers
- program.with_dispatchers((
- // Your dispatchers
- ));
-
- // Execute
- program.exec();
-}
-```
diff --git a/docs/pages/3-features/3-comp.md b/docs/pages/3-features/3-comp.md
deleted file mode 100644
index 259e174..0000000
--- a/docs/pages/3-features/3-comp.md
+++ /dev/null
@@ -1,118 +0,0 @@
-<h1 align="center">Completion</h1>
-<p align="center">
- Mingling's Features
-</p>
-
----
-
-## Enable Feature
-
-`comp` is the command-line completion feature provided by **Mingling**. Its approach is not static completion but rather dynamic completion by invoking your program itself.
-
-Enable this feature as follows:
-
-```toml
-[dependencies]
-mingling = {
- version = "...",
- features = ["comp"]
-}
-```
-
-## Setup
-
-Once `comp` is enabled, `gen_program!` will automatically generate a `CompletionDispatcher`, which is a command with the node `__comp`: the completion script will call this subcommand.
-
-Add this [Dispatcher](pages/2-basic/3-dispatcher) to your [Program](pages/2-basic/1-program):
-
-```rust
-fn main() {
- let mut program = ThisProgram::new();
- program.with_dispatcher(CompletionDispatcher);
- program.exec();
-}
-```
-
-## Usage
-
-You can use the `completion!` macro to bind completion logic to your command entry point. The syntax is as follows:
-
-```rust
-// Define Dispatcher
-dispatcher!("test-comp",
- TestCompletionCommand => TestCompletionEntry
-);
-
-// Establish completion logic, bound to `TestCompletionEntry`
-#[completion(TestCompletionEntry)]
-fn comp_test_comp_cmd(_ctx: &ShellContext) -> Suggest {
- suggest!()
-}
-```
-
-You can obtain the context passed by the shell via `ShellContext` and return the generated suggestions:
-
-```rust
-#[completion(TestCompletionEntry)]
-fn comp_test_comp_cmd(ctx: &ShellContext) -> Suggest {
- if ctx.current_word.starts_with("-") {
- // Comp flags
- return suggest!(
- "--name": "Names",
- "--age": "Age"
- );
- }
-
- if ctx.previous_word == "--name" {
- return suggest!("Bob", "Alice"); // Comp names
- }
-
- if ctx.previous_word == "--age" {
- return suggest!(); // If typing age, suggest nothing
- }
-
- suggest!() // Comp nothing
-}
-```
-
-> 🎬 Logic
->
-> When the user inputs `bin test-<TAB>`, it completes to `bin test-comp`.
->
-> When the user inputs `bin test-comp -<TAB>`, it suggests `--age` / `--name`.
->
-> When the user inputs `bin test-comp --name <TAB>`, it suggests `Bob` / `Alice`.
->
-> In other cases, no suggestions are generated.
-
-## Generate Completion Script
-
-Any shell requires registering a relevant completion script to enable your command's completion capability. However, **Mingling** provides a related build script:
-
-Please add the following to `build-dependencies` in your `Cargo.toml`:
-
-```toml
-[build-dependencies]
-mingling = { version = "...", features = ["comp"] }
-```
-
-Next, call the following logic in your project's `build.rs`:
-
-```rust
-use mingling::build::build_comp_scripts;
-
-fn main() {
- // Generate completion scripts for the current program
- // build_comp_scripts().unwrap();
-
- // Or specify a specific name
- build_comp_scripts("your_cmd").unwrap();
-}
-```
-
-`build_comp_scripts` will generate the corresponding completion scripts based on your platform and output them to the `target` directory.
-
-> [!Note]
-> The completion script does not contain the actual completion logic;
->
-> it is just a thin invocation layer.
diff --git a/docs/pages/3-features/4-async.md b/docs/pages/3-features/4-async.md
deleted file mode 100644
index 08cbb9a..0000000
--- a/docs/pages/3-features/4-async.md
+++ /dev/null
@@ -1,6 +0,0 @@
-<h1 align="center">Async</h1>
-<p align="center">
- Mingling's Features
-</p>
-
----
diff --git a/docs/pages/3-features/5-clap-parser.md b/docs/pages/3-features/5-clap-parser.md
deleted file mode 100644
index 5cb68e6..0000000
--- a/docs/pages/3-features/5-clap-parser.md
+++ /dev/null
@@ -1,6 +0,0 @@
-<h1 align="center">Clap Parser</h1>
-<p align="center">
- Mingling's Features
-</p>
-
----