aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
Diffstat (limited to 'docs')
-rw-r--r--docs/_zh_CN/pages/2-implementing-fallbacks.md2
-rw-r--r--docs/_zh_CN/pages/3-parsing-complex-arguments.md40
-rw-r--r--docs/pages/1-creating-your-first-program.md2
-rw-r--r--docs/pages/3-parsing-complex-arguments.md90
-rw-r--r--docs/res/ci_banner.txt12
5 files changed, 79 insertions, 67 deletions
diff --git a/docs/_zh_CN/pages/2-implementing-fallbacks.md b/docs/_zh_CN/pages/2-implementing-fallbacks.md
index a7c04d0..e4fd3f8 100644
--- a/docs/_zh_CN/pages/2-implementing-fallbacks.md
+++ b/docs/_zh_CN/pages/2-implementing-fallbacks.md
@@ -133,7 +133,7 @@ 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
```
-
+
以上便是 **Mingling** 的回退机制,在接下来的章节中,您将学习如何使用 `Picker` 解析复杂的用户输入。
<p align="center" style="font-size: 0.85em; color: gray;">
diff --git a/docs/_zh_CN/pages/3-parsing-complex-arguments.md b/docs/_zh_CN/pages/3-parsing-complex-arguments.md
index 923218e..4ee9cec 100644
--- a/docs/_zh_CN/pages/3-parsing-complex-arguments.md
+++ b/docs/_zh_CN/pages/3-parsing-complex-arguments.md
@@ -12,7 +12,7 @@
```rust
let name = args.first().cloned().unwrap_or_else(|| "World".to_string());
```
-
+
而本章节将会引入新的 **Mingling** 特性:`Picker`,它提供轻量且和 **Mingling** 类型路由高度契合的命令解析方案。
要启用 `Picker`,您需要修改 `Cargo.toml` ✏️
@@ -24,7 +24,7 @@ mingling = {
features = ["parser"]
}
```
-
+
好了,多的不说,让我们上手编辑代码,重写前文的解析代码 ✏️
```rust
@@ -40,7 +40,7 @@ fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
ResultGreetSomeone::new(name)
}
```
-
+
`Picker` 为所有 `Into<Vec<String>>` 实现了 `pick` `pick_or` `pick_or_route` 函数:它们可以语义化地从字符串列表中 **拾取 (Pick)** 参数,并转换为结构化数据。
对于上述示例中的代码:
@@ -48,7 +48,7 @@ fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
```rust
prev.pick_or((), "World").unpack();
```
-
+
它的语义为:
```rust
@@ -60,7 +60,7 @@ prev.pick_or((), "World").unpack();
// | |______________________ 拾取或使用默认
// |___________________________ 从前一个输入中
```
-
+
## 解析标志参数
若您的程序设计需要解析标志参数 (例如:`greet --name Alice`),可以使用如下方式:
@@ -68,7 +68,7 @@ prev.pick_or((), "World").unpack();
```rust
prev.pick_or(["--name", "-n"], "World").unpack();
```
-
+
同理,它的语义为:
```rust
@@ -80,7 +80,7 @@ prev.pick_or(["--name", "-n"], "World").unpack();
// | |____________________________________ 拾取或使用默认
// |_________________________________________ 从前一个输入中
```
-
+
## 关于 `.unpack()` 💡
您可能注意到了,`Picker` 在命令解析的最后,会执行一个 `.unpack()` 函数,它的作用是将前面解析出来的结果,转换为结构化信息。
@@ -94,10 +94,10 @@ let (name, age, id) = prev
.pick::<u8>(["--age", "-a"])
.pick::<u32>(["--id", "-I"])
.unpack();
-
+
// 可解析参数 --name Alice --age 21 --id 0711251
```
-
+
> [!IMPORTANT]
> `Picker` 对解析顺序极其敏感,特别是位置参数:因为它是顺序解析的
>
@@ -146,7 +146,7 @@ fn render_greet_someone(prev: ResultGreetSomeone) {
r_println!("Hello, {}!", *prev);
}
```
-
+
若使用 `pick_or_route`,写法会变得相对复杂:因为 `.unpack()` 不再直接返回参数,而是 `Result<Value, Route>`
不过 **Mingling** 提供了简化展开的宏 `route!`,它不复杂,只是省略了一部分样板代码:
@@ -160,7 +160,7 @@ let name = match pick_result {
Err(e) => return e,
};
```
-
+
## 提取值的后处理
在您使用 `pick` 提取了用户输入后,可以使用 `after` 或 `after_or_route` 立刻处理该参数 ✏️
@@ -182,7 +182,7 @@ fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
ResultGreetSomeone::new(name) // 此处传入的 name 已被格式化处理
}
```
-
+
同样,您可以使用 `after_or_route` 来处理输入参数的格式错误 ✏️
```rust
@@ -227,7 +227,7 @@ fn render_greet_someone(prev: ResultGreetSomeone) {
r_println!("Hello, {}!", *prev);
}
```
-
+
## 布尔值解析
`Picker` 当然也可以解析 **布尔类型**,但是布尔类型分为显式和隐式模式,
@@ -251,7 +251,7 @@ fn handle_some_entry(prev: SomeEntry) -> NextProcess {
// 其他逻辑
}
```
-
+
## 特殊用法:`usize` 解析
**Mingling** 为 `usize` 提供了一个特殊的用法:解析类似 `25G`、`32mb` 等字样 ✏️
@@ -264,7 +264,7 @@ fn parse_size() {
assert_eq!(size, 25 * 1024 * 1024);
}
```
-
+
## 自定义可解析类型
您可以使用 `Pickable` trait 使您的类型支持被 `Picker` 解析,这也是 `Picker` 拓展性的来源 ✏️
@@ -292,7 +292,7 @@ impl Pickable for Address {
}
}
```
-
+
我们为 `Address` 实现 `Pickable`:接下来我们便可以使用 `ip:port` 的方式来输入参数了 ✏️
```rust
@@ -312,14 +312,14 @@ fn render_connected(prev: ResultConnected) {
r_println!("Connected: IP: {} PORT: {}", addr.ip, addr.port);
}
```
-
+
执行效果如下:
```bash
~> your-bin connect --addr 127.0.0.1:8080
Connected: IP: 127.0.0.1 PORT: 8080
```
-
+
## 自动为枚举实现 Pickable
要为枚举类型实现 `Pickable` trait,无需手动实现:`Picker` 会为所有实现了 `PickableEnum` 的类型实现 `Pickable`,只需要该枚举类型实现了 `EnumTag` ✏️
@@ -339,7 +339,7 @@ pub enum Fruits {
// 为 Fruits 实现 PickableEnum
impl PickableEnum for Fruits {}
```
-
+
接下来您便可以直接使用 `Picker` 解析该类型 ✏️
```rust
@@ -356,7 +356,7 @@ fn render_ate_fruit(prev: ResultFruit) {
r_println!("Picked fruit: {:?}", *prev);
}
```
-
+
以上便是 `Picker` 的所有用法,在下一章节,我会介绍如何在 **Mingling** 内为命令实现帮助文档。
<p align="center" style="font-size: 0.85em; color: gray;">
diff --git a/docs/pages/1-creating-your-first-program.md b/docs/pages/1-creating-your-first-program.md
index 40690c7..0a6ff3c 100644
--- a/docs/pages/1-creating-your-first-program.md
+++ b/docs/pages/1-creating-your-first-program.md
@@ -250,7 +250,7 @@ Hello, World!
~> your-bin greet Alice
Hello, Alice!
```
-
+
At this point, you have successfully created a basic **Mingling** command-line program. The next chapter will explain how to implement a fallback mechanism for your command-line program to handle cases where a command or renderer does not exist.
<p align="center" style="font-size: 0.85em; color: gray;">
diff --git a/docs/pages/3-parsing-complex-arguments.md b/docs/pages/3-parsing-complex-arguments.md
index 8cd5503..b48b28b 100644
--- a/docs/pages/3-parsing-complex-arguments.md
+++ b/docs/pages/3-parsing-complex-arguments.md
@@ -12,7 +12,7 @@
```rust
let name = args.first().cloned().unwrap_or_else(|| "World".to_string());
```
-
+
This chapter introduces a new **Mingling** feature: `Picker`. It provides a lightweight parsing solution that meshes well with **Mingling**'s typed routing.
To enable `Picker`, edit `Cargo.toml` ✏️
@@ -24,7 +24,7 @@ mingling = {
features = ["parser"]
}
```
-
+
Enough talk, let's get coding and rewrite the parsing logic from the prev. section ✏️
```rust
@@ -33,14 +33,14 @@ fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
// Prev. approach:
// let args = prev.inner;
// let name = args.first().cloned().unwrap_or_else(|| "World".to_string());
-
+
// New approach with Picker
let name = prev.pick_or((), "World").unpack();
-
+
ResultGreetSomeone::new(name)
}
```
-
+
`Picker` implements `pick`, `pick_or`, and `pick_or_route` for anything `Into<Vec<String>>`. These functions let you semantically **pick** args from a string list and convert them into structured data.
In the code above:
@@ -48,7 +48,7 @@ fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
```rust
prev.pick_or((), "World").unpack();
```
-
+
Its meaning:
```rust
@@ -60,7 +60,7 @@ prev.pick_or((), "World").unpack();
// | |______________________ pick or use default
// |___________________________ from the prev. input
```
-
+
## Parsing Flag Args
If your app needs to parse flag args (e.g., `greet --name Alice`), do:
@@ -68,7 +68,7 @@ prev.pick_or((), "World").unpack();
```rust
prev.pick_or(["--name", "-n"], "World").unpack();
```
-
+
Its meaning:
```rust
@@ -80,7 +80,7 @@ prev.pick_or(["--name", "-n"], "World").unpack();
// | |____________________________________ pick or use default
// |_________________________________________ from the prev. input
```
-
+
## About `.unpack()` 💡
You may have noticed `Picker` calls `.unpack()` at the end of parsing. It converts the parsed result into structured info.
@@ -94,10 +94,10 @@ let (name, age, id) = prev
.pick::<u8>(["--age", "-a"])
.pick::<u32>(["--id", "-I"])
.unpack();
-
+
// Parses: --name Alice --age 21 --id 0711251
```
-
+
> [!IMPORTANT]
> `Picker` is very order-sensitive, esp. with positional args: it parses sequentially.
>
@@ -113,10 +113,10 @@ let (name, age, id) = prev
```rust
dispatcher!("greet", GreetCommand => GreetEntry);
-
+
pack!(ResultGreetSomeone = String);
pack!(ErrorGreetNoNameProvided = ());
-
+
#[chain]
fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
// Use `pick_or_route` to extract the `--name` arg
@@ -128,39 +128,39 @@ fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
)
// After using any routable method, `unpack` returns `Result<Value, Route>`
.unpack();
-
+
// Use the `route!` macro to expand `pick_result`,
// If it's `Err`, the chain returns here, routing to the specified type
let name = route!(pick_result);
ResultGreetSomeone::new(name).to_chain()
}
-
+
// Handles rendering for `ErrorGreetNoNameProvided`
#[renderer]
fn render_err_greet_no_name_provided(_prev: ErrorGreetNoNameProvided) {
r_println!("Error: No name provided.")
}
-
+
#[renderer]
fn render_greet_someone(prev: ResultGreetSomeone) {
r_println!("Hello, {}!", *prev);
}
```
-
+
Using `pick_or_route` makes the code a bit more complex: `.unpack()` no longer returns the value directly, but `Result<Value, Route>`.
However, **Mingling** provides the `route!` macro to simplify expansion. It's not complex—just cuts some boilerplate:
```rust
let name = route!(pick_result);
-
+
// Expands to
let name = match pick_result {
Ok(r) => r,
Err(e) => return e,
};
```
-
+
## Post-Processing Extracted Values
After using `pick` to extract user input, you can use `after` or `after_or_route` to process the arg immediately ✏️
@@ -178,19 +178,19 @@ fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
.to_string()
})
.unpack();
-
+
ResultGreetSomeone::new(name) // name is now formatted
}
```
-
+
Similarly, use `after_or_route` to handle format errors in input args ✏️
```rust
dispatcher!("greet", GreetCommand => GreetEntry);
-
+
pack!(ResultGreetSomeone = String);
pack!(ErrorGreetNameTooLong = usize);
-
+
#[chain]
fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
let pick_result = prev
@@ -201,7 +201,7 @@ fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
.to_lowercase()
.trim()
.to_string();
-
+
// Check name length, route to error type if too long
let len = name.len();
if len < 32 {
@@ -212,22 +212,22 @@ fn handle_greet_entry(prev: GreetEntry) -> NextProcess {
})
.unpack();
let name = route!(pick_result);
-
+
ResultGreetSomeone::new(name).to_chain()
}
-
+
#[renderer]
fn render_error_greet_name_too_long(prev: ErrorGreetNameTooLong) {
let len = *prev;
r_println!("Error: name too long (length: {} > 32)", len);
}
-
+
#[renderer]
fn render_greet_someone(prev: ResultGreetSomeone) {
r_println!("Hello, {}!", *prev);
}
```
-
+
## Parsing Booleans
`Picker` can parse **bool** types too, but with both explicit and implicit modes:
@@ -247,11 +247,11 @@ fn render_greet_someone(prev: ResultGreetSomeone) {
fn handle_some_entry(prev: SomeEntry) -> NextProcess {
let confirmed: bool = prev.pick::<Yes>(()).unpack().is_yes();
let confirm: bool = prev.pick::<bool>(["--confirm", "-C"]).unpack();
-
+
// other logic
}
```
-
+
## Special Use: `usize` Parsing
**Mingling** has a special use for `usize`: parsing strings like `25G`, `32mb`, etc. ✏️
@@ -264,7 +264,7 @@ fn parse_size() {
assert_eq!(size, 25 * 1024 * 1024);
}
```
-
+
## Custom Parsable Types
Use the `Pickable` trait to make your types parsable by `Picker`. This is where `Picker`'s extensibility comes from ✏️
@@ -276,50 +276,50 @@ pub struct Address {
ip: String,
port: u16,
}
-
+
impl Pickable for Address {
type Output = Self;
fn pick(args: &mut Argument, flag: Flag) -> Option<Self::Output> {
// Extract raw string from Argument using Flag
let raw = args.pick_argument(flag)?;
-
+
// Parse raw string into structured data
let parts: Vec<&str> = raw.split(':').collect();
let ip = parts.first()?.to_string();
let port: u16 = parts.get(1)?.parse().ok()?;
-
+
Some(Address { ip, port })
}
}
```
-
+
With `Pickable` implemented for `Address`, we can now use `ip:port` format for input ✏️
```rust
dispatcher!("connect", ConnectCommand => ConnectEntry);
-
+
pack!(ResultConnected = Address);
-
+
#[chain]
fn handle_connect_entry(prev: ConnectEntry) -> NextProcess {
let address: Address = prev.pick("--addr").unpack();
ResultConnected::new(address)
}
-
+
#[renderer]
fn render_connected(prev: ResultConnected) {
let addr = prev.inner;
r_println!("Connected: IP: {} PORT: {}", addr.ip, addr.port);
}
```
-
+
Running it:
```bash
~> your-bin connect --addr 127.0.0.1:8080
Connected: IP: 127.0.0.1 PORT: 8080
```
-
+
## Auto-Implementing Pickable for Enums
No need to manually implement `Pickable` for enums: `Picker` auto-implements it for any type that implements `PickableEnum`, as long as it also implements `EnumTag` ✏️
@@ -335,28 +335,28 @@ pub enum Fruits {
Banana,
Orange,
}
-
+
// Implement PickableEnum for Fruits
impl PickableEnum for Fruits {}
```
-
+
Now you can directly use `Picker` to parse this type ✏️
```rust
pack!(ResultFruit = Fruits);
-
+
#[chain]
fn handle_eat_fruit_entry(prev: EatFruitEntry) -> NextProcess {
let fruit: Fruits = prev.pick("--fruit").unpack();
ResultFruit::new(fruit)
}
-
+
#[renderer]
fn render_ate_fruit(prev: ResultFruit) {
r_println!("Picked fruit: {:?}", *prev);
}
```
-
+
That's all for `Picker`'s usage. In the next chapter, I'll introduce how to implement help docs for commands in **Mingling**.
<p align="center" style="font-size: 0.85em; color: gray;">
diff --git a/docs/res/ci_banner.txt b/docs/res/ci_banner.txt
new file mode 100644
index 0000000..78f574b
--- /dev/null
+++ b/docs/res/ci_banner.txt
@@ -0,0 +1,12 @@
+ __ __ __ __ __ ______ ______
+ / \ / |/ | / | / | / \ / |
+ ██ \ /██ |██/ _______ ______ ██ | ██/ _______ ______ /██████ |██████/
+ ███ \ /███ |/ |/ \ / \ ██ | / |/ \ / \ ██ | ██/ ██ |
+ ████ /████ |██ |███████ |/██████ | ██ | ██ |███████ |/██████ | ██ | ██ |
+ ██ ██ ██/██ |██ |██ | ██ |██ | ██ | ██ | ██ |██ | ██ |██ | ██ | ██ | __ ██ |
+ ██ |███/ ██ |██ |██ | ██ |██ \__██ | ██ |_____ ██ |██ | ██ |██ \__██ | ██ \__/ | _██ |_
+ ██ | █/ ██ |██ |██ | ██ |██ ██ | ██ |██ |██ | ██ |██ ██ | ██ ██/ / ██ |
+ ██/ ██/ ██/ ██/ ██/ ███████ | ████████/ ██/ ██/ ██/ ███████ | ██████/ ██████/
+ / \__██ | / \__██ |
+ ██ ██/ ██ ██/
+ ██████/ ██████/