aboutsummaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-08-17 04:05:41 +0800
committer魏曹先生 <1992414357@qq.com>2026-08-17 04:05:41 +0800
commitaa251efb87b561f62266628f06ad341253fbbdc5 (patch)
tree7d2cc2f34f3e4a7282d8d6f0ec0f5ae4bfc87002 /docs
parentc23c590330af83afb6e146bcd9b0a274b3689d22 (diff)
refactor!: remove legacy parser feature and migrate to picker
The legacy `parser` feature and its module tree (`mingling::parser`, `Argument`, `Picker`, `Pickable`, etc.) have been fully removed and replaced by the `picker` feature powered by `arg-picker`. BREAKING CHANGE: Remove `parser` feature and use `picker` instead. Migration guide: Replace `features = ["parser"]` with `features = ["picker"]` and update API usage per the provided table.
Diffstat (limited to 'docs')
-rw-r--r--docs/_zh_CN/pages/3-define-a-chain.md2
-rw-r--r--docs/_zh_CN/pages/6-argument-parse-picker.md246
-rw-r--r--docs/_zh_CN/pages/other/features.md12
-rw-r--r--docs/dev/pages/abouts/code-verify-system.md4
-rw-r--r--docs/example-pages/examples.json30
-rw-r--r--docs/pages/1-getting-started.md2
-rw-r--r--docs/pages/3-define-a-chain.md2
-rw-r--r--docs/pages/6-argument-parse-picker.md243
-rw-r--r--docs/pages/other/features.md14
9 files changed, 213 insertions, 342 deletions
diff --git a/docs/_zh_CN/pages/3-define-a-chain.md b/docs/_zh_CN/pages/3-define-a-chain.md
index bf30b91..c8d8a24 100644
--- a/docs/_zh_CN/pages/3-define-a-chain.md
+++ b/docs/_zh_CN/pages/3-define-a-chain.md
@@ -92,7 +92,7 @@ fn handle_greet(args: EntryGreet) -> Next {
}
```
-如果你启用了 `parser` 特性,还可以用 `Picker` 做更灵活的参数提取,不过那是后话了。
+如果你启用了 `picker` 特性,还可以用 `Picker` 做更灵活的参数提取,不过那是后话了。
## 组合起来
diff --git a/docs/_zh_CN/pages/6-argument-parse-picker.md b/docs/_zh_CN/pages/6-argument-parse-picker.md
index 33f1f0d..7944d0a 100644
--- a/docs/_zh_CN/pages/6-argument-parse-picker.md
+++ b/docs/_zh_CN/pages/6-argument-parse-picker.md
@@ -19,34 +19,38 @@ let name = args.first().cloned().unwrap_or_else(|| "World".to_string());
```toml
# Cargo.toml
[dependencies.mingling]
-features = ["parser"]
+features = ["picker"]
```
好了,让我们看看 `Picker` 的写法:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("greet", EntryGreet);
@@@pack!(ResultName = String);
#[chain]
fn handle_greet_entry(prev: EntryGreet) -> Next {
- let name = prev.pick_or((), "World").unpack();
+ let name = prev
+ .pick_or(&arg![String], || "World".to_string())
+ .unwrap();
ResultName::new(name).into()
}
```
-`AsPicker` 为所有可以转换为 `Vec<String>` 的类型实现了 `pick`、`pick_or`、`pick_or_route` 函数:它们可以语义化地从字符串列表中 **拾取 (Pick)** 参数,并转换为结构化数据。
+`EntryPicker` 为所有入口类型实现了 `pick`、`pick_or`、`pick_or_default` 和 `pick_or_route` 函数:它们可以通过 `arg!` 宏声明要拾取的内容,语义化地从字符串列表中 **拾取 (Pick)** 参数,并转换为结构化数据。
对于上述示例中的代码:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("greet", EntryGreet);
@@@pack!(ResultName = String);
@@@#[chain]
@@@fn handle_greet_entry(prev: EntryGreet) -> Next {
-let name = prev.pick_or((), "World").unpack();
+let name = prev
+ .pick_or(&arg![String], || "World".to_string())
+ .unwrap();
@@@ResultName::new(name).into()
@@@}
```
@@ -54,75 +58,79 @@ let name = prev.pick_or((), "World").unpack();
它的语义为:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("greet", EntryGreet);
@@@pack!(ResultName = String);
@@@#[chain]
@@@fn handle_greet_entry(prev: EntryGreet) {
@@@let name: String =
- prev.pick_or((), "World").unpack();
-// ~~~~ ~~~~~~~ ~~ ~~~~~~~ ~~~~~~~~
-// | | | | |_ 解包为 String
-// | | | |__________ 默认值为 "World"
-// | | |______________ 取出第一个位置参数(不指定标志)
-// | |______________________ 拾取或使用默认
-// |___________________________ 从前一个输入中
+ prev.pick_or(&arg![String], || "World".to_string()).unwrap();
+// ~~~~ ~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+// | | | | |_ 解包为 String
+// | | | |__________________________ 默认值为 "World"
+// | | |________________________________________ 取出第一个位置参数(声明为 String)
+// | |________________________________________________ 拾取或使用默认
+// |_____________________________________________________ 从前一个输入中
@@@}
```
## 解析标志参数
-若你的程序需要解析标志参数(例如 `greet --name Alice`),可以使用如下方式
+若你的程序需要解析标志参数(例如 `greet --name Alice`),可以在 `arg!` 中声明一个具名标志:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("greet", EntryGreet);
@@@pack!(ResultName = String);
#[chain]
fn handle_greet_entry(prev: EntryGreet) -> Next {
- let name = prev.pick_or(["--name", "-n"], "World").unpack();
+ let name = prev
+ .pick_or(&arg![name: String, 'n'], || "World".to_string())
+ .unwrap();
ResultName::new(name).into()
}
```
+`arg!` 宏会从字段名推导长标志名(`--name`),`'n'` 则添加短别名(`-n`)。
+
同理,它的语义为:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("greet", EntryGreet);
@@@pack!(ResultName = String);
@@@#[chain]
@@@fn handle_greet_entry(prev: EntryGreet) {
@@@let name: String =
- prev.pick_or(["--name", "-n"], "World").unpack();
-// ~~~~ ~~~~~~~ ~~~~~~~~~~~~~~~~ ~~~~~~~ ~~~~~~~~
-// | | | | |_ 解包为 String
-// | | | |__________ 默认值为 "World"
-// | | |____________________________ 取出 "--name" 或 "-n" 后面的参数
-// | |____________________________________ 拾取或使用默认
-// |_________________________________________ 从前一个输入中
+ prev.pick_or(&arg![name: String, 'n'], || "World".to_string()).unwrap();
+// ~~~~ ~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+// | | | | |_ 解包为 String
+// | | | |________________________ 默认值为 "World"
+// | | |___________________________________________________ 取出 "--name" 或 "-n" 后面的参数
+// | |___________________________________________________________ 拾取或使用默认
+// |________________________________________________________________ 从前一个输入中
@@@}
```
-## 关于 `.unpack()`
+## 关于 `.unwrap()` 与 `route!`
-你可能注意到了,`Picker` 在命令解析的最后,会执行一个 `.unpack()` 函数,它的作用是将前面解析出来的结果,转换为结构化信息。
+你可能注意到了,`Picker` 在命令解析的最后,会执行一个 `.unwrap()`(或 `route!`)函数,它的作用是将前面解析出来的结果,转换为结构化信息。
-对于只拾取了一次的数据来说,`.unpack()` 会返回单个数据,而对于多次拾取,`Picker` 则会返回元组:
+对于只拾取了一次的数据来说,`.unwrap()` 会返回单个数据,而对于多次拾取,`Picker` 则会返回元组:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("test", EntryTest);
@@@pack!(ResultInfo = (String, u8, u32));
#[chain]
fn handle_test_entry(prev: EntryTest) -> Next {
let (name, age, id) = prev
- .pick::<String>(["--name", "-n"])
- .pick::<u8>(["--age", "-a"])
- .pick::<u32>(["--id", "-I"])
- .unpack();
+ .pick_or_default(&arg![name: String, 'n'])
+ .pick_or_default(&arg![age: u8, 'a'])
+ .pick_or_default(&arg![id: u32, 'I'])
+ .unwrap();
ResultInfo::new((name, age, id)).into()
}
@@ -138,7 +146,7 @@ fn handle_test_entry(prev: EntryTest) -> Next {
先来看一个简单示例
```rust
-// Features: ["parser", "extras"]
+// Features: ["picker", "extras"]
@@@use mingling::macros::buffer;
@@@use mingling::macros::route;
@@@dispatcher!("greet", EntryGreet);
@@ -147,12 +155,13 @@ fn handle_test_entry(prev: EntryTest) -> Next {
#[chain]
fn handle_greet_entry(prev: EntryGreet) -> Next {
- let pick_result = prev
- .pick_or_route(["--name", "-n"], ErrorNoName::default())
- .unpack();
-
- // 使用 route! 宏展开 pick_result
- let name = route!(pick_result);
+ // 使用 route! 宏展开 Result<Value, Route>
+ let name = route!(
+ prev.pick_or_route(&arg![name: String, 'n'], || {
+ ErrorNoName::default().to_chain()
+ })
+ .to_result()
+ );
ResultName::new(name).into()
}
@@ -162,18 +171,18 @@ fn render_greet(result: ResultName) {
}
```
-若使用 `pick_or_route`,写法会变得相对复杂:因为 `.unpack()` 不再直接返回参数,而是 `Result<Value, Route>`。
+若使用 `pick_or_route`,`.to_result()` 不再直接返回参数,而是 `Result<Value, Route>`。
不过 **Mingling** 的 `extras` 特性提供了简化展开的宏 `route!`,它不复杂,只是省略了一部分样板代码:
```rust
-// Features: ["parser", "extras"]
+// Features: ["picker", "extras"]
@@@ pack!(ErrorFail = ());
@@@ use mingling::macros::route;
+@@@ use mingling::picker::IntoPicker;
@@@ fn func() -> mingling::ChainProcess<ThisProgram> {
@@@ let args: Vec<String> = vec![];
-@@@ let pick_result = args.pick_or_route::<String, _>((), ErrorFail::new(())).unpack();
-let name = route!(pick_result);
+let name = route!(args.pick_or_route(&arg![String], || ErrorFail::new(()).to_chain()).to_result());
@@@ mingling::macros::empty_result!()
@@@ }
```
@@ -181,14 +190,14 @@ let name = route!(pick_result);
它展开为:
```rust
-// Features: ["parser", "extras"]
+// Features: ["picker", "extras"]
@@@ pack!(ErrorFail = ());
+@@@ use mingling::picker::IntoPicker;
@@@ fn func() -> mingling::ChainProcess<ThisProgram> {
@@@ let args: Vec<String> = vec![];
-@@@ let pick_result = args.pick_or_route::<String, _>((), ErrorFail::new(())).unpack();
-let name = match pick_result {
+let name = match args.pick_or_route(&arg![String], || ErrorFail::new(()).to_chain()).to_result() {
Ok(r) => r,
- Err(e) => return e.to_chain(),
+ Err(e) => return e,
};
@@@ mingling::macros::empty_result!()
@@@ }
@@ -196,122 +205,59 @@ let name = match pick_result {
## 提取值的后处理
-在您使用 `pick` 提取了用户输入后,可以使用 `after` 立刻处理该参数
+在您使用 `pick` 提取了用户输入后,可以使用 `post` 立刻处理该参数
-````rust
-// Features: ["parser"]
+```rust
+// Features: ["picker"]
@@@dispatcher!("greet", EntryGreet);
@@@pack!(ResultName = String);
#[chain]
fn handle_greet_entry(prev: EntryGreet) -> Next {
let name = prev
- .pick_or(["--name", "-n"], "World")
+ .pick_or(&arg![name: String, 'n'], || "World".to_string())
// 在提取出 --name 后,立刻格式化
- .after(|name: String| {
+ .post(|name: String| {
name.replace(['-', '_', '.'], " ")
.to_lowercase()
.trim()
.to_string()
})
- .unpack();
-
- ResultName::new(name).into()
-}
-```
-
-同样,你可以使用 `after_or_route` 来处理输入参数的格式错误
-
-```rust
-// Features: ["parser", "extras"]
-@@@use mingling::macros::buffer;
-@@@use mingling::macros::route;
-@@@dispatcher!("greet", EntryGreet);
-@@@pack!(ResultName = String);
-@@@pack!(ErrorNameTooLong = usize);
-
-#[chain]
-fn handle_greet_entry(prev: EntryGreet) -> Next {
- let pick_result = prev
- .pick_or(["--name", "-n"], "World")
- .after_or_route(|name: &String| {
- if name.len() < 32 {
- Ok(name.clone())
- } else {
- Err(ErrorNameTooLong::new(name.len()))
- }
- })
- .unpack();
- let name = route!(pick_result);
+ .unwrap();
ResultName::new(name).into()
}
-
-#[renderer(buffer)]
-fn render_name_too_long(prev: ErrorNameTooLong) {
- let len = *prev;
- r_println!("Error: name too long (length: {} > 32)", len);
-}
-
-#[renderer(buffer)]
-fn render_name(prev: ResultName) {
- r_println!("Hello, {}!", *prev);
-}
```
## 布尔值解析
-`Picker` 当然也可以解析布尔类型,但是布尔类型分为显式和隐式模式:
-
-| 模式 | 格式 |
-| ---- | ----------------------------------- |
-| 隐式 | `--confirmed` |
-| 显式 | `--confirm true` 或 `--confirm yes` |
-
-- 使用 `.pick::<bool>(flag)` 时,采用隐式解析:只要标志存在即为 `true`
-- 使用 `.pick::<Yes>(flag)` 或 `.pick::<True>(flag)` 时,采用显式解析
-
-一般来说使用隐式解析即可,但在处理重要的确认行为时,显式逻辑更符合语义。
+`Picker` 将布尔值解析为**标志**:标志存在即为 `true`。
```rust
-// Features: ["parser"]
-@@@use mingling::parser::Yes;
+// Features: ["picker"]
+@@@use mingling::picker::value::Flag;
@@@dispatcher!("test", EntryTest);
@@@pack!(ResultDone = ());
#[chain]
fn handle_entry(prev: EntryTest) -> Next {
-@@@ let prev1 = prev.clone();
- let _confirmed: bool = prev.pick::<Yes>(()).unpack().is_yes();
-@@@ let prev = prev1;
- let _confirm: bool = prev.pick::<bool>(["--confirm", "-C"]).unpack();
+ // `--confirm` / `-C` 存在 → true
+ let _confirm: bool = *prev.pick(&arg![confirm: Flag, 'C']).unwrap();
ResultDone::default().to_render()
}
```
-## 特殊用法:`usize` 解析
-
-**Mingling** 为 `usize` 提供了一个特殊的用法:解析类似 `25G`、`32mib` 等字样
+> [!NOTE]
+> 对于重要的确认行为,如果精确的布尔语义很关键,请将标志与显式的值检查配合使用。
-```rust
-// Features: ["parser"]
-
-#[test]
-fn parse_size() {
- let vec = vec!["--size".to_string(), "25mib".to_string()];
- let size: usize = vec.pick(["--size", "-S"]).unpack();
- assert_eq!(size, 25 * 1024 * 1024);
-}
-```
-
## 自定义可解析类型
-你可以使用 `Pickable` trait 使你的类型支持被 `Picker` 解析,这也是 `Picker` 拓展性的来源
+你可以使用 `SinglePickable` trait 使你的类型支持被 `Picker` 解析,这也是 `Picker` 拓展性的来源
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@use mingling::macros::buffer;
-@@@use mingling::parser::{Pickable, Argument};
+@@@use mingling::picker::{PickerArgResult, SinglePickable};
@@@use mingling::Flag;
#[derive(Default, Clone)]
pub struct Address {
@@ -319,14 +265,18 @@ pub struct Address {
port: u16,
}
-impl Pickable for Address {
- type Output = Self;
- fn pick(args: &mut Argument, flag: Flag) -> Option<Self::Output> {
- let raw = args.pick_argument(flag)?;
+impl SinglePickable for Address {
+ fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+ let Some(raw) = str else {
+ return PickerArgResult::NotFound;
+ };
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 })
+ let ip = parts.first().copied().unwrap_or_default().to_string();
+ let port: u16 = match parts.get(1).and_then(|p| p.parse().ok()) {
+ Some(p) => p,
+ None => return PickerArgResult::NotFound,
+ };
+ PickerArgResult::Parsed(Address { ip, port })
}
}
@@@dispatcher!("connect", EntryConnect);
@@ -334,7 +284,7 @@ impl Pickable for Address {
#[chain]
fn handle_connect_entry(prev: EntryConnect) -> Next {
- let address: Address = prev.pick("--addr").unpack();
+ let address: Address = prev.pick_or_default(&arg![Address]).unwrap();
ResultConnected::new(address).into()
}
@@ -351,14 +301,14 @@ fn render_connected(addr: ResultConnected) {
Connected: IP: 127.0.0.1 PORT: 8080
```
-## 自动为枚举实现 Pickable
+## 为枚举实现 Pickable
-要为枚举类型实现 `Pickable`,只需该枚举实现了 `EnumTag`,然后为其实现 `PickableEnum` 即可
+要让枚举支持 `Picker` 解析,可以手写 `SinglePickable`,用 match 匹配输入:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@use mingling::macros::buffer;
-@@@use mingling::parser::PickableEnum;
+@@@use mingling::picker::{PickerArgResult, SinglePickable};
@@@use mingling::EnumTag;
#[derive(Debug, Default, EnumTag)]
pub enum Fruits {
@@ -368,13 +318,26 @@ pub enum Fruits {
Orange,
}
-impl PickableEnum for Fruits {}
+impl SinglePickable for Fruits {
+ fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+ let Some(str) = str else {
+ return PickerArgResult::NotFound;
+ };
+ let fruit = match str.to_lowercase().as_str() {
+ "apple" => Self::Apple,
+ "banana" => Self::Banana,
+ "orange" => Self::Orange,
+ _ => return PickerArgResult::NotFound,
+ };
+ PickerArgResult::Parsed(fruit)
+ }
+}
@@@dispatcher!("eat", EntryEat);
@@@pack!(ResultFruit = Fruits);
#[chain]
fn handle_eat_entry(prev: EntryEat) -> Next {
- let fruit: Fruits = prev.pick("--fruit").unpack();
+ let fruit: Fruits = prev.pick_or_default(&arg![Fruits]).unwrap();
ResultFruit::new(fruit).into()
}
@@ -389,4 +352,3 @@ fn render_fruit(prev: ResultFruit) {
<p align="center" style="font-size: 0.85em; color: gray;">
Written by @Weicao-CatilGrass
</p>
-````
diff --git a/docs/_zh_CN/pages/other/features.md b/docs/_zh_CN/pages/other/features.md
index 4913945..30231ce 100644
--- a/docs/_zh_CN/pages/other/features.md
+++ b/docs/_zh_CN/pages/other/features.md
@@ -330,24 +330,12 @@ analyze_and_build_type_mapping().unwrap();
详见 [示例](https://mingling-rs.github.io/mingling/docs/example-viewer.html?name=example-pathfinder)
-## 特性 `parser`
-
-**介绍:**
-
-启用参数解析器模块,提供参数解析功能。
-
-开启后可以使用 `Picker` 进行简易的参数提取,支持 `pick()` 和 `pick_or()` 等方法。
-
-详见 [示例](https://mingling-rs.github.io/mingling/docs/example-viewer.html?name=example-argument-parse)
-
## 特性 `picker`
**介绍:**
引入依赖 `arg-picker`,为 Mingling 提供更高级的参数解析能力。
-它可以与 `parser`、`clap` 特性共存,但建议不要和 `parser` 特性同时启用,因为两者的 API 极为相似。
-
`picker` 是独立于 Mingling 的参数解析器,不依赖 `mingling_core` 的内置参数提取 API。
详见 [示例](https://mingling-rs.github.io/mingling/docs/example-viewer.html?name=example-argument-picker)
diff --git a/docs/dev/pages/abouts/code-verify-system.md b/docs/dev/pages/abouts/code-verify-system.md
index 61b66e8..929b337 100644
--- a/docs/dev/pages/abouts/code-verify-system.md
+++ b/docs/dev/pages/abouts/code-verify-system.md
@@ -209,7 +209,7 @@ Use `@@@` for:
````markdown
```rust
-// Features: ["parser"]
+// Features: ["picker"]
// Dependencies:
// serde = "1"
@@ -237,6 +237,6 @@ mingling::macros::gen_program!();
```toml
[dependencies]
-mingling = { path = "../../mingling", features = ["parser"] }
+mingling = { path = "../../mingling", features = ["picker"] }
serde = { version = "1", features = ["derive"] }
```
diff --git a/docs/example-pages/examples.json b/docs/example-pages/examples.json
index 31336f1..8d6eaa6 100644
--- a/docs/example-pages/examples.json
+++ b/docs/example-pages/examples.json
@@ -16,21 +16,6 @@
]
},
{
- "id": "example-argument-parse",
- "name": "Argument Parse",
- "icon": "📋",
- "category": "parsing",
- "desc": "Shows how to use Mingling's `parser` feature with a `Picker` to extract and validate typed arguments from the command line.\n",
- "tags": [
- "pick",
- "Pickable"
- ],
- "files": [
- "src/main.rs",
- "Cargo.toml"
- ]
- },
- {
"id": "example-argument-picker",
"name": "Argument Picker",
"icon": "📋",
@@ -144,21 +129,6 @@
]
},
{
- "id": "example-custom-pickable",
- "name": "Custom Pickable",
- "icon": "🎯",
- "category": "parsing",
- "desc": "Shows how to implement the `Pickable` trait on custom types for seamless extraction from CLI arguments via Picker.\n",
- "tags": [
- "Pickable",
- "custom"
- ],
- "files": [
- "src/main.rs",
- "Cargo.toml"
- ]
- },
- {
"id": "example-dispatch-tree",
"name": "Dispatch Tree",
"icon": "🌳",
diff --git a/docs/pages/1-getting-started.md b/docs/pages/1-getting-started.md
index 0131ec1..94f507e 100644
--- a/docs/pages/1-getting-started.md
+++ b/docs/pages/1-getting-started.md
@@ -27,7 +27,7 @@ Some features **directly affect the entire lifecycle behavior**, so you need to
[dependencies.mingling]
version = "0.5.0"
features = [
- "parser",
+ "picker",
"comp",
]
```
diff --git a/docs/pages/3-define-a-chain.md b/docs/pages/3-define-a-chain.md
index dca299e..72ade94 100644
--- a/docs/pages/3-define-a-chain.md
+++ b/docs/pages/3-define-a-chain.md
@@ -92,7 +92,7 @@ fn handle_greet(args: EntryGreet) -> Next {
}
```
-If you enable the `parser` feature, you can also use `Picker` for more flexible param extraction — but that's a topic for later.
+If you enable the `picker` feature, you can also use `Picker` for more flexible param extraction — but that's a topic for later.
## Putting It Together
diff --git a/docs/pages/6-argument-parse-picker.md b/docs/pages/6-argument-parse-picker.md
index 9da56d5..da0fff1 100644
--- a/docs/pages/6-argument-parse-picker.md
+++ b/docs/pages/6-argument-parse-picker.md
@@ -19,34 +19,38 @@ To enable `Picker`, update your `Cargo.toml`:
```toml
# Cargo.toml
[dependencies.mingling]
-features = ["parser"]
+features = ["picker"]
```
Now let's see how `Picker` is written:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("greet", EntryGreet);
@@@pack!(ResultName = String);
#[chain]
fn handle_greet_entry(prev: EntryGreet) -> Next {
- let name = prev.pick_or((), "World").unpack();
+ let name = prev
+ .pick_or(&arg![String], || "World".to_string())
+ .unwrap();
ResultName::new(name).into()
}
```
-`AsPicker` implements `pick`, `pick_or`, and `pick_or_route` for all types convertible to `Vec<String>`. These functions semantically **pick** params from the string list and convert them into structured data.
+`EntryPicker` implements `pick`, `pick_or`, `pick_or_default`, and `pick_or_route` for all entry types. These functions semantically **pick** params from the string list and convert them into structured data, using the `arg!` macro to declare what to pick.
For the code above:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("greet", EntryGreet);
@@@pack!(ResultName = String);
@@@#[chain]
@@@fn handle_greet_entry(prev: EntryGreet) -> Next {
-let name = prev.pick_or((), "World").unpack();
+let name = prev
+ .pick_or(&arg![String], || "World".to_string())
+ .unwrap();
@@@ResultName::new(name).into()
@@@}
```
@@ -54,75 +58,79 @@ let name = prev.pick_or((), "World").unpack();
Its semantics are:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("greet", EntryGreet);
@@@pack!(ResultName = String);
@@@#[chain]
@@@fn handle_greet_entry(prev: EntryGreet) {
@@@let name: String =
- prev.pick_or((), "World").unpack();
-// ~~~~ ~~~~~~~ ~~ ~~~~~~~ ~~~~~~~~
-// | | | | |_ unpack as String
-// | | | |__________ default value "World"
-// | | |______________ pick the first positional arg (no flag)
-// | |______________________ pick or use default
-// |___________________________ from the previous input
+ prev.pick_or(&arg![String], || "World".to_string()).unwrap();
+// ~~~~ ~~~~~~~ ~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+// | | | | |_ unwrap to String
+// | | | |__________________________ default value "World"
+// | | |________________________________________ pick the first positional arg (declared as `String`)
+// | |________________________________________________ pick or use default
+// |_____________________________________________________ from the previous input
@@@}
```
## Parsing Flag Arguments
-If your program needs to parse flag arguments (e.g. `greet --name Alice`), do this:
+If your program needs to parse flag arguments (e.g. `greet --name Alice`), declare a named flag in `arg!`:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("greet", EntryGreet);
@@@pack!(ResultName = String);
#[chain]
fn handle_greet_entry(prev: EntryGreet) -> Next {
- let name = prev.pick_or(["--name", "-n"], "World").unpack();
+ let name = prev
+ .pick_or(&arg![name: String, 'n'], || "World".to_string())
+ .unwrap();
ResultName::new(name).into()
}
```
+The `arg!` macro derives the long flag name (`--name`) from the field name, and `'n'` adds the short alias (`-n`).
+
Its semantics:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("greet", EntryGreet);
@@@pack!(ResultName = String);
@@@#[chain]
@@@fn handle_greet_entry(prev: EntryGreet) {
@@@let name: String =
- prev.pick_or(["--name", "-n"], "World").unpack();
-// ~~~~ ~~~~~~~ ~~~~~~~~~~~~~~~~ ~~~~~~~ ~~~~~~~~
-// | | | | |_ unpack as String
-// | | | |__________ default value "World"
-// | | |____________________________ pick the value after "--name" or "-n"
-// | |____________________________________ pick or use default
-// |_________________________________________ from the previous input
+ prev.pick_or(&arg![name: String, 'n'], || "World".to_string()).unwrap();
+// ~~~~ ~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~
+// | | | | |_ unwrap to String
+// | | | |________________________ default value "World"
+// | | |___________________________________________________ pick the value after "--name" or "-n"
+// | |___________________________________________________________ pick or use default
+// |________________________________________________________________ from the previous input
@@@}
```
-## About `.unpack()`
+## About `.unwrap()` and `route!`
-You may have noticed that `Picker` calls `.unpack()` at the end of parsing. It converts the collected results into structured info.
+You may have noticed that `Picker` calls `.unwrap()` (or `route!`) at the end of parsing. It converts the collected results into structured info.
-For a single pick, `.unpack()` returns the value directly; for multiple picks, it returns a tuple:
+For a single pick, `.unwrap()` returns the value directly; for multiple picks, it returns a tuple:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("test", EntryTest);
@@@pack!(ResultInfo = (String, u8, u32));
#[chain]
fn handle_test_entry(prev: EntryTest) -> Next {
let (name, age, id) = prev
- .pick::<String>(["--name", "-n"])
- .pick::<u8>(["--age", "-a"])
- .pick::<u32>(["--id", "-I"])
- .unpack();
+ .pick_or_default(&arg![name: String, 'n'])
+ .pick_or_default(&arg![age: u8, 'a'])
+ .pick_or_default(&arg![id: u32, 'I'])
+ .unwrap();
ResultInfo::new((name, age, id)).into()
}
@@ -138,7 +146,7 @@ As the saying goes: "never trust your users." To handle missing required params,
Here's a simple example:
```rust
-// Features: ["parser", "extras"]
+// Features: ["picker", "extras"]
@@@use mingling::macros::buffer;
@@@use mingling::macros::route;
@@@dispatcher!("greet", EntryGreet);
@@ -147,12 +155,13 @@ Here's a simple example:
#[chain]
fn handle_greet_entry(prev: EntryGreet) -> Next {
- let pick_result = prev
- .pick_or_route(["--name", "-n"], ErrorNoName::default())
- .unpack();
-
- // Use route! macro to expand pick_result
- let name = route!(pick_result);
+ // Use route! macro to expand the Result<Value, Route>
+ let name = route!(
+ prev.pick_or_route(&arg![name: String, 'n'], || {
+ ErrorNoName::default().to_chain()
+ })
+ .to_result()
+ );
ResultName::new(name).into()
}
@@ -162,18 +171,18 @@ fn render_greet(result: ResultName) {
}
```
-With `pick_or_route`, the code becomes more involved: `.unpack()` no longer returns the value directly, but `Result<Value, Route>`.
+With `pick_or_route`, `.to_result()` no longer returns the value directly, but `Result<Value, Route>`.
However, **Mingling**'s `extras` feature provides the `route!` macro for simplified expansion. It's not complex — it just reduces boilerplate:
```rust
-// Features: ["parser", "extras"]
+// Features: ["picker", "extras"]
@@@ pack!(ErrorFail = ());
@@@ use mingling::macros::route;
+@@@ use mingling::picker::IntoPicker;
@@@ fn func() -> mingling::ChainProcess<ThisProgram> {
@@@ let args: Vec<String> = vec![];
-@@@ let pick_result = args.pick_or_route::<String, _>((), ErrorFail::new(())).unpack();
-let name = route!(pick_result);
+let name = route!(args.pick_or_route(&arg![String], || ErrorFail::new(()).to_chain()).to_result());
@@@ mingling::macros::empty_result!()
@@@ }
```
@@ -181,14 +190,14 @@ let name = route!(pick_result);
It expands to:
```rust
-// Features: ["parser", "extras"]
+// Features: ["picker", "extras"]
@@@ pack!(ErrorFail = ());
+@@@ use mingling::picker::IntoPicker;
@@@ fn func() -> mingling::ChainProcess<ThisProgram> {
@@@ let args: Vec<String> = vec![];
-@@@ let pick_result = args.pick_or_route::<String, _>((), ErrorFail::new(())).unpack();
-let name = match pick_result {
+let name = match args.pick_or_route(&arg![String], || ErrorFail::new(()).to_chain()).to_result() {
Ok(r) => r,
- Err(e) => return e.to_chain(),
+ Err(e) => return e,
};
@@@ mingling::macros::empty_result!()
@@@ }
@@ -196,122 +205,59 @@ let name = match pick_result {
## Post-processing Extracted Values
-After picking user input with `pick`, you can use `after` to process it immediately:
+After picking user input with `pick`, you can use `post` to process it immediately:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@dispatcher!("greet", EntryGreet);
@@@pack!(ResultName = String);
#[chain]
fn handle_greet_entry(prev: EntryGreet) -> Next {
let name = prev
- .pick_or(["--name", "-n"], "World")
+ .pick_or(&arg![name: String, 'n'], || "World".to_string())
// Format immediately after picking --name
- .after(|name: String| {
+ .post(|name: String| {
name.replace(['-', '_', '.'], " ")
.to_lowercase()
.trim()
.to_string()
})
- .unpack();
+ .unwrap();
ResultName::new(name).into()
}
```
-Similarly, you can use `after_or_route` to handle input format errors:
-
-```rust
-// Features: ["parser", "extras"]
-@@@use mingling::macros::buffer;
-@@@use mingling::macros::route;
-@@@dispatcher!("greet", EntryGreet);
-@@@pack!(ResultName = String);
-@@@pack!(ErrorNameTooLong = usize);
-
-#[chain]
-fn handle_greet_entry(prev: EntryGreet) -> Next {
- let pick_result = prev
- .pick_or(["--name", "-n"], "World")
- .after_or_route(|name: &String| {
- if name.len() < 32 {
- Ok(name.clone())
- } else {
- Err(ErrorNameTooLong::new(name.len()))
- }
- })
- .unpack();
- let name = route!(pick_result);
-
- ResultName::new(name).into()
-}
-
-#[renderer(buffer)]
-fn render_name_too_long(prev: ErrorNameTooLong) {
- let len = *prev;
- r_println!("Error: name too long (length: {} > 32)", len);
-}
-
-#[renderer(buffer)]
-fn render_name(prev: ResultName) {
- r_println!("Hello, {}!", *prev);
-}
-```
-
## Boolean Parsing
-`Picker` can also parse booleans, in two modes:
-
-| Mode | Format |
-| -------- | ----------------------------------- |
-| Implicit | `--confirmed` |
-| Explicit | `--confirm true` or `--confirm yes` |
-
-- `.pick::<bool>(flag)` uses implicit mode: the flag being present means `true`
-- `.pick::<Yes>(flag)` or `.pick::<True>(flag)` uses explicit mode
-
-Implicit mode is generally sufficient, but for important confirmations, explicit logic is more idiomatic.
+`Picker` parses booleans as **flags**: the flag being present means `true`.
```rust
-// Features: ["parser"]
-@@@use mingling::parser::Yes;
+// Features: ["picker"]
+@@@use mingling::picker::value::Flag;
@@@dispatcher!("test", EntryTest);
@@@pack!(ResultDone = ());
#[chain]
fn handle_entry(prev: EntryTest) -> Next {
-@@@ let prev1 = prev.clone();
- let _confirmed: bool = prev.pick::<Yes>(()).unpack().is_yes();
-@@@ let prev = prev1;
- let _confirm: bool = prev.pick::<bool>(["--confirm", "-C"]).unpack();
+ // `--confirm` / `-C` present → true
+ let _confirm: bool = *prev.pick(&arg![confirm: Flag, 'C']).unwrap();
ResultDone::default().to_render()
}
```
-## Special Usage: `usize` Parsing
-
-**Mingling** provides a special `usize` feature: parsing strings like `25G`, `32mib`, etc.
+> [!NOTE]
+> For important confirmations, pair the flag with an explicit value check if the exact boolean semantics matter.
-```rust
-// Features: ["parser"]
-
-#[test]
-fn parse_size() {
- let vec = vec!["--size".to_string(), "25mib".to_string()];
- let size: usize = vec.pick(["--size", "-S"]).unpack();
- assert_eq!(size, 25 * 1024 * 1024);
-}
-```
-
## Custom Pickable Types
-You can make your types pickable by `Picker` using the `Pickable` trait — this is where `Picker`'s extensibility comes from.
+You can make your types pickable by `Picker` using the `SinglePickable` trait — this is where `Picker`'s extensibility comes from.
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@use mingling::macros::buffer;
-@@@use mingling::parser::{Pickable, Argument};
+@@@use mingling::picker::{PickerArgResult, SinglePickable};
@@@use mingling::Flag;
#[derive(Default, Clone)]
pub struct Address {
@@ -319,14 +265,18 @@ pub struct Address {
port: u16,
}
-impl Pickable for Address {
- type Output = Self;
- fn pick(args: &mut Argument, flag: Flag) -> Option<Self::Output> {
- let raw = args.pick_argument(flag)?;
+impl SinglePickable for Address {
+ fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+ let Some(raw) = str else {
+ return PickerArgResult::NotFound;
+ };
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 })
+ let ip = parts.first().copied().unwrap_or_default().to_string();
+ let port: u16 = match parts.get(1).and_then(|p| p.parse().ok()) {
+ Some(p) => p,
+ None => return PickerArgResult::NotFound,
+ };
+ PickerArgResult::Parsed(Address { ip, port })
}
}
@@@dispatcher!("connect", EntryConnect);
@@ -334,7 +284,7 @@ impl Pickable for Address {
#[chain]
fn handle_connect_entry(prev: EntryConnect) -> Next {
- let address: Address = prev.pick("--addr").unpack();
+ let address: Address = prev.pick_or_default(&arg![Address]).unwrap();
ResultConnected::new(address).into()
}
@@ -351,14 +301,14 @@ Output:
Connected: IP: 127.0.0.1 PORT: 8080
```
-## Auto-implementing Pickable for Enums
+## Implementing Pickable for Enums
-To make an enum `Pickable`, just implement `EnumTag` on it, then implement `PickableEnum`:
+To make an enum pickable, implement `SinglePickable` manually with a match on the input:
```rust
-// Features: ["parser"]
+// Features: ["picker"]
@@@use mingling::macros::buffer;
-@@@use mingling::parser::PickableEnum;
+@@@use mingling::picker::{PickerArgResult, SinglePickable};
@@@use mingling::EnumTag;
#[derive(Debug, Default, EnumTag)]
pub enum Fruits {
@@ -368,13 +318,26 @@ pub enum Fruits {
Orange,
}
-impl PickableEnum for Fruits {}
+impl SinglePickable for Fruits {
+ fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
+ let Some(str) = str else {
+ return PickerArgResult::NotFound;
+ };
+ let fruit = match str.to_lowercase().as_str() {
+ "apple" => Self::Apple,
+ "banana" => Self::Banana,
+ "orange" => Self::Orange,
+ _ => return PickerArgResult::NotFound,
+ };
+ PickerArgResult::Parsed(fruit)
+ }
+}
@@@dispatcher!("eat", EntryEat);
@@@pack!(ResultFruit = Fruits);
#[chain]
fn handle_eat_entry(prev: EntryEat) -> Next {
- let fruit: Fruits = prev.pick("--fruit").unpack();
+ let fruit: Fruits = prev.pick_or_default(&arg![Fruits]).unwrap();
ResultFruit::new(fruit).into()
}
diff --git a/docs/pages/other/features.md b/docs/pages/other/features.md
index 4552d30..3779390 100644
--- a/docs/pages/other/features.md
+++ b/docs/pages/other/features.md
@@ -330,23 +330,11 @@ analyze_and_build_type_mapping().unwrap();
See [example](https://mingling-rs.github.io/mingling/docs/example-viewer.html?name=example-pathfinder)
-## Feature `parser`
-
-**Description:**
-
-Enables the argument parser module, providing argument parsing functionality.
-
-When enabled, you can use `Picker` for simple argument extraction, supporting methods like `pick()` and `pick_or()`.
-
-See [example](https://mingling-rs.github.io/mingling/docs/example-viewer.html?name=example-argument-parse)
-
## Feature `picker`
**Description:**
-Introduces the `arg-picker` dependency, providing more advanced argument parsing capabilities for Mingling.
-
-It can coexist with the `parser` and `clap` features, but it is recommended not to enable it alongside the `parser` feature, as their APIs are very similar.
+Introduces the `arg-picker` dependency, providing advanced argument parsing capabilities for Mingling.
`picker` is an argument parser independent of Mingling and does not rely on the built-in argument extraction API of `mingling_core`.