aboutsummaryrefslogtreecommitdiff
path: root/docs/_zh_CN
diff options
context:
space:
mode:
Diffstat (limited to 'docs/_zh_CN')
-rw-r--r--docs/_zh_CN/README.md1
-rw-r--r--docs/_zh_CN/_sidebar.md3
-rw-r--r--docs/_zh_CN/index.html73
-rw-r--r--docs/_zh_CN/pages/.gitignore1
-rw-r--r--docs/_zh_CN/pages/1-creating-your-first-program.md256
-rw-r--r--docs/_zh_CN/pages/2-implementing-fallbacks.md139
6 files changed, 473 insertions, 0 deletions
diff --git a/docs/_zh_CN/README.md b/docs/_zh_CN/README.md
new file mode 100644
index 0000000..218a8de
--- /dev/null
+++ b/docs/_zh_CN/README.md
@@ -0,0 +1 @@
+__
diff --git a/docs/_zh_CN/_sidebar.md b/docs/_zh_CN/_sidebar.md
new file mode 100644
index 0000000..cc43930
--- /dev/null
+++ b/docs/_zh_CN/_sidebar.md
@@ -0,0 +1,3 @@
+- [Welcome!](README)
+* [Creating your first Program](pages/1-creating-your-first-program)
+* [Implementing Fallbacks](pages/2-implementing-fallbacks)
diff --git a/docs/_zh_CN/index.html b/docs/_zh_CN/index.html
new file mode 100644
index 0000000..80ed30d
--- /dev/null
+++ b/docs/_zh_CN/index.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<html lang="zh-Hans">
+ <head>
+ <meta charset="utf-8" />
+ <meta
+ name="viewport"
+ content="width=device-width, initial-scale=1, minimum-scale=1.0, shrink-to-fit=no, viewport-fit=cover"
+ />
+
+ <title>Mingling 中文文档</title>
+ <meta
+ name="Mingling Documents"
+ content="Quick start with Mingling and build your command-line program!"
+ />
+
+ <link rel="stylesheet" href="../css/vue.css" />
+ </head>
+
+ <body>
+ <nav>
+ <a href="https://docs.rs/mingling/latest/mingling/">Docs.rs</a>
+ <a href="https://crates.io/crates/mingling">Crates.io</a>
+ |
+ <a href="https://github.com/CatilGrass/mingling">GitHub</a>
+ |
+ <a href="https://git.catilgrass.cn/catilgrass/mingling.git"
+ ><b>Source</b></a
+ >
+ |
+ <a href="../"><b>EN Docs</b></a>
+ </nav>
+ <div id="app"></div>
+
+ <script>
+ window.$docsify = {
+ name: "Mingling 中文文档",
+ auto2top: true,
+ loadSidebar: true,
+ maxLevel: 0,
+ subMaxLevel: 3,
+ search: {
+ placeholder: "Search",
+ noData: "No matches found.",
+ depth: 2,
+ },
+ plugins: [
+ [
+ "flexible-alerts",
+ {
+ style: "flat",
+ labels: {
+ NOTE: "笔记",
+ TIP: "小贴士",
+ IMPORTANT: "重点",
+ WARNING: "注意",
+ CAUTION: "警告",
+ },
+ },
+ ],
+ ],
+ };
+ </script>
+
+ <script src="../scripts/docsify.min.js"></script>
+
+ <script src="../scripts/zoom-image.min.js"></script>
+ <script src="../scripts/search.js"></script>
+ <script src="../scripts/prism-bash.min.js"></script>
+ <script src="../scripts/prism-toml.min.js"></script>
+ <script src="../scripts/prism-rust.js"></script>
+ <script src="../scripts/docsify-plugin-flexible-alerts.js"></script>
+ </body>
+</html>
diff --git a/docs/_zh_CN/pages/.gitignore b/docs/_zh_CN/pages/.gitignore
new file mode 100644
index 0000000..dd33554
--- /dev/null
+++ b/docs/_zh_CN/pages/.gitignore
@@ -0,0 +1 @@
+.obsidian
diff --git a/docs/_zh_CN/pages/1-creating-your-first-program.md b/docs/_zh_CN/pages/1-creating-your-first-program.md
new file mode 100644
index 0000000..27a7f6d
--- /dev/null
+++ b/docs/_zh_CN/pages/1-creating-your-first-program.md
@@ -0,0 +1,256 @@
+<h1 align="center">创建您的第一个程序</h1>
+<p align="center">
+ 了解 <b>Mingling</b>,并使用它创建您的第一个命令行程序
+</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/_zh_CN/pages/2-implementing-fallbacks.md b/docs/_zh_CN/pages/2-implementing-fallbacks.md
new file mode 100644
index 0000000..071a5d1
--- /dev/null
+++ b/docs/_zh_CN/pages/2-implementing-fallbacks.md
@@ -0,0 +1,139 @@
+<h1 align="center">实现回退机制</h1>
+<p align="center">
+ 使用回退机制处理程序的错误情况
+</p>
+
+## 书接上文
+
+ 在上文中,我们介绍了如何使用 **Mingling** 开发基本的命令行程序:你可以使用 `"greet"` 子命令输出 `"Hello, World!"`,也可以使用 `"greet Alice"` 输出 `"Hello, Alice!"`
+
+ 而当用户并未输入 `"greet"` 时呢?让我们键入命令尝试一下 ⌨️
+
+```bash
+~> your-bin hello
+~> your-bin hello Alice
+```
+
+ **它没有任何反应!** 👆
+
+ 让我来解释为什么:**Mingling** 不自作主张,无论发生什么它都不会输出内容到终端(除了 `unwind` 下的 `panic!`)
+
+ 这意味着,如果您需要在命令行程序出错时能够主动地做些什么,你就得显式声明。
+
+ 好在 **Mingling** 提供了较为方便的接口实现该功能:在 `gen_program!` 宏中,会生成两个 `FallBack` 类型
+
+|类型|发生时机|发生方式|
+|-|-|-|
+|RendererNotFound|调度无法找到的渲染器时|作为 `Chain` 调度|
+|DispatcherNotFound|输入命令但无法匹配分发器|作为 `Chain` 调度|
+
+### `DispatcherNotFound` 类型
+
+ 首先让我们关注 `DispatcherNotFound` 类型,它的产生方式如下:
+
+```rust
+// 1. 定义 `greet` 命令
+dispatcher!("greet", GreetCommand => GreetEntry);
+
+fn main() {
+ // ->> 用户输入 "hello Alice"
+ let mut program = ThisProgram::new();
+
+ // 2. 导入 `greet` 命令
+ program.with_dispatcher(GreetCommand);
+
+ // 3. 执行程序
+ program.exec();
+}
+
+// ...
+
+// 5. 接收 DispatcherNotFound 调度
+#[renderer]
+fn dispatcher_not_found(prev: DispatcherNotFound) {
+ // 6. 输出
+ r_println!(
+ "Cannot match any command! Current input: \"{}\"",
+ prev.join(" ")
+ );
+}
+
+// 4. 无法匹配到任何名为 `hello` 的分发器
+// 将用户参数原样分发到 DispatcherNotFound
+gen_program!();
+```
+
+ 上述程序的运行效果为:
+
+```bash
+~> omg hello
+Cannot match any command! Current input: "hello"
+
+~> omg hello Alice
+Cannot match any command! Current input: "hello Alice"
+```
+
+ 现在若用户输入了不匹配的命令,**Mingling** 将会输出对应的内容!
+
+## `RendererNotFound` 类型
+
+ `RendererNotFound` 有两种可能产生:
+
+ 1. 该类型被显式分发到了 `Renderer` (使用 `.to_render()` 函数),但是该类型未实现渲染器
+ 2. 该类型被分发到了 `Chain`,但是该类型未实现链,也未实现渲染器
+
+ 一般来说,`RendererNotFound` **不应该在业务逻辑中产生**:它被调度意味着您的类型需要被渲染但是并不能被渲染。您可以使用该类型来定位哪个类型缺失渲染器实现 ✏️
+
+```rust
+dispatcher!("greet", GreetCommand => GreetEntry);
+
+fn main() {
+ let mut program = ThisProgram::new();
+
+ program.with_dispatcher(GreetCommand);
+ program.exec();
+}
+
+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)
+}
+
+// 让我们故意去除 `ResultGreetSomeone` 类型的渲染器实现
+// #[renderer]
+// fn render_greet_someone(prev: ResultGreetSomeone) {
+// r_println!("Hello, {}!", *prev);
+// }
+
+#[renderer]
+fn renderer_not_found(prev: RendererNotFound) {
+ if *prev == "DispatcherNotFound" {
+ return; // 排除 "DispatcherNotFound" 类型
+ }
+
+ // 当未找到渲染器时触发 `panic!`
+ panic!("Renderer \"{}\" not found!", *prev);
+}
+
+gen_program!();
+
+```
+
+ 上述程序的运行效果为:
+
+```bash
+~> your-bin greet Alice
+
+thread 'main' (90772) panicked at src/bin/your-bin.rs:30:5:
+Renderer "ResultGreetSomeone" not found!
+note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
+```
+
+<p align="center" style="font-size: 0.85em; color: gray;">
+ Written by @Weicao-CatilGrass
+</p>