The Picker2 Arguments Parser
A smarter, faster alternative to Picker
## Intro
Mingling's `parser` feature is a temporary argument parsing solution created in the early stages of the project. While it can handle basic argument parsing tasks, its functionality is incomplete and has many limitations.
This article aims to propose the design and development plan for the new Picker2 (feature name: `picker`).
### Picker2 Expected Syntax
1. **Pos args & flag args no longer depend on parsing order:** In `parser`, all flag args must be parsed before pos args. Picker2 removes this restriction.
2. **Declare flags with declarative macros:** Use `positional!()`, `flag![-X, --x]` etc. to declare flags — cleaner and more concise.
3. **One-shot `parse()` replaces step-by-step `unpack()`:** Defers the entire parsing flow to the final step, leaving more room for compile-time optimization.
```rust
#[chain]
fn handle_hello(args: EntryHello) {
let parsed = args
.pick::(positional!())
.pick::(flag![--help, -h])
.parse();
}
```
4. **Post-processing & routing control:** Use `after`, `after_route` to control post-processing logic. The route type is explicitly specified by the first `route` method.
```rust
#[chain]
fn handle_hello(args: EntryHello) {
let parsed = args
.pick::(positional!())
.after(|v| format!("\"{}\"", v)) // post-processing
.parse();
}
```
```rust
#[chain]
fn handle_hello(args: EntryHello) -> Next {
// requires impl Groupped<_>
// |
let parsed = args // vvvvvvvvvvvvvvvvvvv
.pick_route::(positional!(), ErrorNoNameProvided)
.after_route(|v| Ok(format!("\"{}\"", v))) // post-process & route
.parse();
let routed_parsed = route!(parsed);
empty_result!()
}
```
5. **Keep the `Pickable` Trait**
6. **Keep `PickableEnum` & provide a derive macro**
7. **Use `pick_optional`, `pick_vec` instead of implementing trait separately for `Option`, `Vec`**
8. **Use `pick_flag` instead of `pick::`**
9. **Provide `YesOrNo`, `TrueOrFalse` etc. that implement the `BoolFlag` trait for explicit bool extraction**
10. **Provide a `MultiFlag` derive macro, supporting arg modes like `pacman`**
```rust
#[derive(MultiFlag)]
pub struct SomeToggles {
// default [-i]
pub install: bool,
#[flag('U')]
pub uninstall: bool,
#[flag('X')]
pub execute: bool,
}
// Auto-implements Pickable, supports parsing args like `-iUX`
```
11. **Support multiple arg formats:** e.g. `--key=value`, `/Key=Value`, controlled by global config `PickerConfig` (or: `Picker::from_with_cfg(prev.inner, PickerConfig::default())`)
12. `.parse()` no longer returns a bare tuple, but instead returns:
```rust
pub struct PickerResult {
pub result: Tuple,
pub remains_argument: Arguments,
}
```
---
## Current Progress (2026-07-14)
Picker2 has been significantly implemented. Here is the current status:
### Completed
#### Core Type System
- **`PickerArgs`** — Three-tier ownership enum for CLI arguments:
- `Slice(&'a [&'a str])` — zero-copy borrow from `std::env::args()`
- `Vec(Vec<&'a str>)` — owned container, borrowed elements
- `Owned(Vec)` — fully owned (constructed / manipulated args)
- **`PickerResult`** — Four-state parse result:
- `Unparsed` — not yet parsed (default)
- `Parsed(Type)` — successfully parsed
- `NotFound` — requested value not found
- `FormatError` — input could not be parsed
- **`PickerFlag`** — Const-compatible parameter constraint definition:
```rust
pub struct PickerFlag<'a, Type: Pickable> {
pub full: &'a [&'a str], // flag names & aliases
pub short: Option, // short name, e.g. 'n'
pub positional: bool, // positional or named
pub internal_type: PhantomData,
}
```
All fields are `pub`, enabling direct struct literal construction.
#### `internal_repeat!` Template Engine
A custom proc-macro template engine that generates repetitive code without nightly features or external dependencies.
**Syntax:**
```rust
internal_repeat!(1..=32 => {
// $ — current counter (1, 2, ..., 32)
// $+ — current + 1
// $- — current - 1
// $^ — loop minimum
// ^$ — loop maximum
// ident$ → ident{current}
// ident$+ → ident{current+1}
// ident$- → ident{current-1}
// ident$^ → ident{min}
// (group,+) — repeat * times (current), `,` separator
// (group,+)+ — repeat *+1 times (current + 1)
// (group,+)- — repeat *-1 times (current - 1)
});
```
Generates `PickerPattern1` through `PickerPattern32` structs, each with `N` typed flag/result fields:
```rust
pub struct PickerPattern3<'a, T1, T2, T3> {
pub args: PickerArgs<'a>,
pub flag_1: &'a PickerFlag<'a, T1>,
pub result_1: PickerResult,
pub flag_2: &'a PickerFlag<'a, T2>,
pub result_2: PickerResult,
pub flag_3: &'a PickerFlag<'a, T3>,
pub result_3: PickerResult,
}
```
#### `flag!` Macro
Declarative parameter definition macro. Expands to a const-compatible struct literal:
```rust
// Syntax variants:
flag![name: String] // named, no short
flag![name: String, 'n'] // named, with short
flag![name: String, 'n', "alias"] // named, with short & alias
flag![String] // positional, no name
flag![String, 'F'] // positional, with short
flag![String, 'o', "output"] // positional, with short & alias
// Expansion (all const-compatible):
::mingling::picker::PickerFlag:: {
full: &["name"],
short: ::std::option::Option::Some('n'),
positional: false,
internal_type: ::std::marker::PhantomData,
}
```
The type part preserves original token spans for IDE support (Rust Analyzer).
#### `Pickable` Trait
Implemented for all numeric types, `String`, `PathBuf`, `Option`.
```rust
pub trait Pickable {
fn pick(raw_str: &str) -> PickerResult;
}
```
#### Type-Safe Chaining
```rust
let p4: PickerPattern4 = args
.pick(&flag![name: String])
.pick(&flag![age: i32])
.pick(&flag![food: String, 'F'])
.pick(&flag![kind: String]);
```
Each `.pick()` call adds one type parameter to the pattern. Parameter count is compile-time enforced — writing 3 `.pick()` calls produces `PickerPattern3`, and trying to assign to `PickerPattern4` fails to compile.
### ❌ Pending
#### `.parse()` — Runtime Parsing Engine
Not yet implemented. The proposed parsing flow:
```rust
let result: (String, i32) = args
.pick(&flag![name: String])
.pick(&flag![age: i32])
.parse();
```
Procedural steps:
1. Allocate a `Vec>` indexed by type position
2. Sort all flags: named flags first, positional flags after
3. Each `PickerFlag`'s `Pickable` scans the argument list and marks its tokens
4. Each `Pickable::pick()` resolves its value into the `Vec`
5. Downcast each `Box` to the concrete type
6. Convert via `Into<(...)>` into the final tuple
Planned companion code generation via `internal_repeat!`:
```rust
internal_repeat!(1..=32 => {
impl<'a, (T$,)+> PickerPattern$<'a, (T$,)+> {
pub fn parse(self) -> ((T$,)+) {
// ...
}
}
});
```
#### `.pick_route()` — Error Routing
Post-processing with route-on-error:
```rust
let parsed = args
.pick_route::(flag![path: PathBuf], ErrorNotFound)
.parse();
```
#### `MultiFlag` — Combined Flags
```rust
#[derive(MultiFlag)]
pub struct Toggles {
pub install: bool, // -i
#[flag('U')]
pub uninstall: bool, // -U
#[flag('X')]
pub execute: bool, // -X
}
// Supports: `-iUX`
```
#### `PickerConfig` — Arg Format Configuration
Support for `--key=value`, `/Key=Value`, etc.
### Summary
| Component | Status |
| ---------------------------------- | ------ |
| `PickerArgs` (3-tier ownership) | ✅ |
| `PickerResult` (4-state) | ✅ |
| `PickerFlag` (const-compatible) | ✅ |
| `Pickable` trait + impls | ✅ |
| `internal_repeat!` template engine | ✅ |
| `flag!` macro | ✅ |
| `PickerPattern1..32` structs | ✅ |
| Chainable `.pick()` (2→31) | ✅ |
| **`.parse()` runtime** | ❌ |
| `.pick_route()` error routing | ❌ |
| `MultiFlag` derive | ❌ |
| `PickerConfig` | ❌ |
| Integration with `#[chain]` | ❌ |