aboutsummaryrefslogtreecommitdiff
path: root/mingling_picker
diff options
context:
space:
mode:
author魏曹先生 <1992414357@qq.com>2026-07-17 11:08:07 +0800
committer魏曹先生 <1992414357@qq.com>2026-07-17 11:11:28 +0800
commit79ec6878877f0fd9246d67d3cd4f8cc2d1200150 (patch)
treee3cd8cfc7ef5cd2a6a7ceb93d9a4b1764fa21b61 /mingling_picker
parente6136f22cff446b16dbebf3b26a6fdea6dbc0e83 (diff)
refactor: rename `mingling_picker` to `arg_picker`
Diffstat (limited to 'mingling_picker')
-rw-r--r--mingling_picker/Cargo.toml17
-rw-r--r--mingling_picker/README.md47
-rw-r--r--mingling_picker/src/arg.rs299
-rw-r--r--mingling_picker/src/builtin.rs4
-rw-r--r--mingling_picker/src/builtin/pick_bool.rs22
-rw-r--r--mingling_picker/src/builtin/pick_flag.rs21
-rw-r--r--mingling_picker/src/builtin/pick_numbers.rs80
-rw-r--r--mingling_picker/src/builtin/pick_string.rs11
-rw-r--r--mingling_picker/src/corebind.rs2
-rw-r--r--mingling_picker/src/corebind/entry_picker.rs131
-rw-r--r--mingling_picker/src/infos.rs446
-rw-r--r--mingling_picker/src/lib.rs62
-rw-r--r--mingling_picker/src/parselib.rs144
-rw-r--r--mingling_picker/src/parselib/arg_matcher.rs142
-rw-r--r--mingling_picker/src/parselib/flag_matcher.rs82
-rw-r--r--mingling_picker/src/parselib/multi_arg_matcher.rs141
-rw-r--r--mingling_picker/src/parselib/pos_matcher.rs71
-rw-r--r--mingling_picker/src/parselib/single_matcher.rs59
-rw-r--r--mingling_picker/src/parselib/style.rs258
-rw-r--r--mingling_picker/src/parselib/utils.rs276
-rw-r--r--mingling_picker/src/pickable.rs91
-rw-r--r--mingling_picker/src/pickable/multi_pickable.rs76
-rw-r--r--mingling_picker/src/pickable/single_pickable.rs69
-rw-r--r--mingling_picker/src/picker.rs432
-rw-r--r--mingling_picker/src/picker/parse.rs177
-rw-r--r--mingling_picker/src/picker/patterns.rs205
-rw-r--r--mingling_picker/src/picker/result.rs56
-rw-r--r--mingling_picker/src/value.rs5
-rw-r--r--mingling_picker/src/value/flag.rs145
-rw-r--r--mingling_picker/src/value/vec_until.rs134
-rw-r--r--mingling_picker/test/Cargo.lock68
-rw-r--r--mingling_picker/test/Cargo.toml9
-rw-r--r--mingling_picker/test/src/lib.rs23
-rw-r--r--mingling_picker/test/src/test.rs10
-rw-r--r--mingling_picker/test/src/test/arg_matcher_test.rs289
-rw-r--r--mingling_picker/test/src/test/basic_test.rs223
-rw-r--r--mingling_picker/test/src/test/multi_arg_test.rs181
-rw-r--r--mingling_picker/test/src/test/multi_value_test.rs66
-rw-r--r--mingling_picker/test/src/test/pos_matcher_test.rs141
-rw-r--r--mingling_picker/test/src/test/priority_test.rs85
-rw-r--r--mingling_picker/test/src/test/route_test.rs200
-rw-r--r--mingling_picker/test/src/test/style_test.rs300
-rw-r--r--mingling_picker/test/src/test/value_flag_test.rs174
-rw-r--r--mingling_picker/test/src/test/value_string_test.rs171
44 files changed, 0 insertions, 5645 deletions
diff --git a/mingling_picker/Cargo.toml b/mingling_picker/Cargo.toml
deleted file mode 100644
index ddab33c..0000000
--- a/mingling_picker/Cargo.toml
+++ /dev/null
@@ -1,17 +0,0 @@
-[package]
-name = "mingling_picker"
-version.workspace = true
-edition.workspace = true
-license.workspace = true
-repository.workspace = true
-authors = ["Weicao-CatilGrass"]
-readme = "README.md"
-description = "Mingling's lightweight argument parser"
-
-[features]
-mingling_support = ["dep:mingling_core", "mingling_picker_macros/mingling_support"]
-
-[dependencies]
-mingling_core = { workspace = true, optional = true }
-mingling_picker_macros.workspace = true
-just_fmt.workspace = true
diff --git a/mingling_picker/README.md b/mingling_picker/README.md
deleted file mode 100644
index db67825..0000000
--- a/mingling_picker/README.md
+++ /dev/null
@@ -1,47 +0,0 @@
-# Mingling Picker
-
-A command-line argument parser for [Mingling](https://github.com/mingling-rs/mingling), enabled by the `mingling/picker` feature.
-
-```toml
-[dependencies.mingling]
-version = "0.3.0"
-features = [
- "picker"
-]
-```
-
-Of course, you can also use it as a standalone crate by replacing `mingling::picker` with `mingling_picker`:
-
-```toml
-[dependencies]
-mingling_picker = "0.3.0"
-```
-
-## Chained Argument Parser
-
-Provides a clean chained-call API for declaring arguments to parse:
-
-```rust
-use mingling_picker::prelude::*;
-
-let args: Vec<&str> = vec!["--name", "Bob", "--age", "24"];
-
-let (name, age) = args
- .pick(&arg![name: String])
- .or(|| "Alice".to_string())
- .pick(&arg![age: i32])
- .or(|| 24)
- .post(|num| num.clamp(0, 120))
- .unwrap();
-
-assert_eq!(name, "Bob".to_string());
-assert_eq!(age, 24);
-```
-
-## Parsing Function Library
-
-Provides a pure function library `parselib` for analyzing the structure of command-line arguments.
-
-```rust
-use mingling_picker::parselib::*;
-```
diff --git a/mingling_picker/src/arg.rs b/mingling_picker/src/arg.rs
deleted file mode 100644
index 78ad539..0000000
--- a/mingling_picker/src/arg.rs
+++ /dev/null
@@ -1,299 +0,0 @@
-use crate::Pickable;
-use std::marker::PhantomData;
-
-/// Represents a constraint definition for a parameter selection.
-///
-/// This structure describes the constraints that a command-line parameter (Picker parameter item)
-/// should satisfy, including its full name list (with aliases), short name form, and whether it is
-/// positional.
-///
-/// # Field Descriptions
-///
-/// - `full`: Full name or alias list. For example, `["config", "cfg"]` means the parameter can be
-/// matched with either `--config` or `--cfg`. Must contain at least one non-empty string.
-///
-/// - `short`: Short name (single character). For example, `Some('c')` means it can be passed using
-/// the `-c` form. If set to `None`, the short name form is not supported.
-///
-/// - `positional`: Whether the parameter is positional (i.e., an argument without a flag).
-/// - `true`: The parameter is positional; it is matched by its position in the command line rather
-/// than by a `--name` or `-n` flag.
-/// - `false`: The parameter is a named (flag-based) parameter.
-///
-/// - `_type`: PhantomData to hold the type parameter.
-#[derive(Default, Clone, Copy)]
-pub struct PickerArg<'a, Type>
-where
- Type: Pickable<'a>,
-{
- /// Full name, may include variant names (aliases), e.g., `["config", "cfg"]`.
- pub full: &'a [&'a str],
-
- /// Short name, e.g., `'c'`.
- pub short: Option<char>,
-
- /// Whether the parameter is positional (no flag, matched by position).
- pub positional: bool,
-
- /// PhantomData to hold the type parameter.
- pub internal_type: PhantomData<Type>,
-}
-
-impl<'a, Type> From<&'a PickerArg<'a, Type>> for PickerArg<'a, Type>
-where
- Type: Pickable<'a>,
-{
- fn from(value: &'a PickerArg<'a, Type>) -> Self {
- PickerArg {
- full: value.full,
- short: value.short,
- positional: value.positional,
- internal_type: PhantomData,
- }
- }
-}
-
-impl<'a, Type> PickerArg<'a, Type>
-where
- Type: Pickable<'a>,
-{
- /// Creates a new `PickerArg` with the provided parameters.
- pub fn new(full: &'a [&'a str], short: Option<char>, positional: bool) -> Self {
- Self {
- full,
- short,
- positional,
- internal_type: PhantomData,
- }
- }
-
- /// Returns the full name list (including aliases).
- pub fn full(&self) -> &'a [&'a str] {
- self.full
- }
-
- /// Returns the short name, if any.
- pub fn short(&self) -> Option<char> {
- self.short
- }
-
- /// Returns whether the parameter is positional.
- ///
- /// If `full` is empty or `short` is `None`, the parameter is considered positional
- /// regardless of the stored value.
- pub fn is_positional(&self) -> bool {
- if self.full.is_empty() && self.short.is_none() {
- true
- } else {
- self.positional
- }
- }
-
- /// Sets the full name list.
- pub fn set_full(&mut self, full: &'a [&'a str]) {
- self.full = full;
- }
-
- /// Sets the short name.
- pub fn set_short(&mut self, short: Option<char>) {
- self.short = short;
- }
-
- /// Sets whether the parameter is positional.
- pub fn set_positional(&mut self, positional: bool) {
- self.positional = positional;
- }
-
- /// Sets the full name list and returns self.
- pub fn with_full(mut self, full: &'a [&'a str]) -> Self {
- self.full = full;
- self
- }
-
- /// Clears the full name list (sets it to an empty slice) and returns self.
- pub fn without_full(mut self) -> Self {
- self.full = &[];
- self
- }
-
- /// Sets the short name to the given character and returns self.
- pub fn with_short(mut self, short: char) -> Self {
- self.short = Some(short);
- self
- }
-
- /// Clears the short name (sets it to None) and returns self.
- pub fn without_short(mut self) -> Self {
- self.short = None;
- self
- }
-
- /// Sets whether the parameter is positional and returns self.
- pub fn with_positional(mut self, positional: bool) -> Self {
- self.positional = positional;
- self
- }
-}
-
-/// Describes the attribute (behavior) of a command-line parameter.
-///
-/// The ordering reflects parse priority (higher = parsed first):
-/// `PositionalMulti < Positional < Flag < Single < Multi`
-#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
-pub enum PickerArgAttr {
- /// Positional argument that accepts multiple values (e.g., multiple input files).
- PositionalMulti,
-
- /// Positional argument matched by its position (e.g., an input file).
- #[default]
- Positional,
-
- /// Boolean flag with no associated value (e.g., `--verbose`).
- Flag,
-
- /// Accepts a single value (e.g., `--name Alice`).
- Single,
-
- /// Accepts multiple values (e.g., `--file a.txt --file b.txt`).
- Multi,
-}
-
-impl PickerArgAttr {
- /// Determines if the given `PickerArg` represents a positional parameter.
- ///
- /// If the flag is positional (determined by `flag.is_positional()`), returns
- /// `PickerArgAttr::Positional`. Otherwise, invokes the `other` closure to
- /// produce and return a `PickerArgAttr`.
- ///
- /// # Parameters
- ///
- /// - `flag`: A reference to the [`PickerArg`] to evaluate.
- /// - `other`: A closure that returns a [`PickerArgAttr`] when the flag is
- /// **not** positional.
- #[inline(always)]
- pub fn positional_or_else<'a, T>(
- flag: &PickerArg<'a, T>,
- other: fn() -> PickerArgAttr,
- ) -> PickerArgAttr
- where
- T: Pickable<'a>,
- {
- if flag.is_positional() {
- PickerArgAttr::Positional
- } else {
- other()
- }
- }
-
- /// Determines if the given `PickerArg` represents a positional parameter and returns
- /// `PickerArgAttr::Positional` if so. Otherwise, returns the provided `default` attribute.
- ///
- /// # Parameters
- ///
- /// - `flag`: A reference to the [`PickerArg`] to evaluate.
- /// - `default`: The [`PickerArgAttr`] to return if the flag is not positional.
- #[inline(always)]
- pub fn positional_or<'a, T>(flag: &PickerArg<'a, T>, default: PickerArgAttr) -> PickerArgAttr
- where
- T: Pickable<'a>,
- {
- if flag.is_positional() {
- PickerArgAttr::Positional
- } else {
- default
- }
- }
-
- /// Determines if the given `PickerArg` represents a positional parameter and returns
- /// `PickerArgAttr::Positional` if so. Otherwise, returns `PickerArgAttr::Single`.
- ///
- /// # Parameters
- ///
- /// - `flag`: A reference to the [`PickerArg`] to evaluate.
- #[inline(always)]
- pub fn positional_or_single<'a, T>(flag: &PickerArg<'a, T>) -> PickerArgAttr
- where
- T: Pickable<'a>,
- {
- if flag.is_positional() {
- PickerArgAttr::Positional
- } else {
- PickerArgAttr::Single
- }
- }
-
- /// Determines if the given `PickerArg` represents a positional parameter and returns
- /// `PickerArgAttr::PositionalMulti` if so. Otherwise, returns `PickerArgAttr::Multi`.
- ///
- /// # Parameters
- ///
- /// - `flag`: A reference to the [`PickerArg`] to evaluate.
- #[inline(always)]
- pub fn positional_or_multi<'a, T>(flag: &PickerArg<'a, T>) -> PickerArgAttr
- where
- T: Pickable<'a>,
- {
- if flag.is_positional() {
- PickerArgAttr::PositionalMulti
- } else {
- PickerArgAttr::Multi
- }
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- #[test]
- fn test_picker_flag_attr_ordering() {
- // Multi > Single > Flag > Positional > PositionalMulti
- assert!(PickerArgAttr::Multi > PickerArgAttr::Single);
- assert!(PickerArgAttr::Multi > PickerArgAttr::Flag);
- assert!(PickerArgAttr::Multi > PickerArgAttr::Positional);
- assert!(PickerArgAttr::Multi > PickerArgAttr::PositionalMulti);
-
- assert!(PickerArgAttr::Single > PickerArgAttr::Flag);
- assert!(PickerArgAttr::Single > PickerArgAttr::Positional);
- assert!(PickerArgAttr::Single > PickerArgAttr::PositionalMulti);
-
- assert!(PickerArgAttr::Flag > PickerArgAttr::Positional);
- assert!(PickerArgAttr::Flag > PickerArgAttr::PositionalMulti);
-
- assert!(PickerArgAttr::Positional > PickerArgAttr::PositionalMulti);
-
- // PartialOrd
- assert!(PickerArgAttr::Multi >= PickerArgAttr::Single);
- assert!(PickerArgAttr::Single >= PickerArgAttr::Flag);
- assert!(PickerArgAttr::Flag >= PickerArgAttr::Positional);
- assert!(PickerArgAttr::Positional >= PickerArgAttr::PositionalMulti);
-
- assert!(PickerArgAttr::PositionalMulti < PickerArgAttr::Positional);
- assert!(PickerArgAttr::Positional < PickerArgAttr::Flag);
- assert!(PickerArgAttr::Flag < PickerArgAttr::Single);
- assert!(PickerArgAttr::Single < PickerArgAttr::Multi);
- }
-
- #[test]
- fn test_picker_flag_attr_sorting() {
- // Sort
- let mut values = vec![
- PickerArgAttr::Flag,
- PickerArgAttr::Single,
- PickerArgAttr::Positional,
- PickerArgAttr::Multi,
- PickerArgAttr::PositionalMulti,
- ];
- values.sort();
- assert_eq!(
- values,
- vec![
- PickerArgAttr::PositionalMulti,
- PickerArgAttr::Positional,
- PickerArgAttr::Flag,
- PickerArgAttr::Single,
- PickerArgAttr::Multi,
- ]
- );
- }
-}
diff --git a/mingling_picker/src/builtin.rs b/mingling_picker/src/builtin.rs
deleted file mode 100644
index e855b08..0000000
--- a/mingling_picker/src/builtin.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-mod pick_bool;
-mod pick_flag;
-mod pick_numbers;
-mod pick_string;
diff --git a/mingling_picker/src/builtin/pick_bool.rs b/mingling_picker/src/builtin/pick_bool.rs
deleted file mode 100644
index ccc4424..0000000
--- a/mingling_picker/src/builtin/pick_bool.rs
+++ /dev/null
@@ -1,22 +0,0 @@
-use crate::parselib::{FlagMatcher, Matcher};
-use crate::pickable_needed::*;
-
-impl<'a> Pickable<'a> for bool {
- fn get_attr(_: &'a PickerArg<'a, Self>) -> PickerArgAttr {
- PickerArgAttr::Flag
- }
-
- fn tag(ctx: TagPhaseContext) -> Vec<usize> {
- FlagMatcher::match_all(ctx.into())
- }
-
- fn pick(raw_strs: &[&str]) -> PickerArgResult<Self> {
- if raw_strs.is_empty() {
- // No matching flag found — signal NotFound so the fallback chain
- // (default → route) gets a chance to run.
- PickerArgResult::NotFound
- } else {
- PickerArgResult::Parsed(true)
- }
- }
-}
diff --git a/mingling_picker/src/builtin/pick_flag.rs b/mingling_picker/src/builtin/pick_flag.rs
deleted file mode 100644
index b642a9a..0000000
--- a/mingling_picker/src/builtin/pick_flag.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-use crate::parselib::{FlagMatcher, Matcher};
-use crate::pickable_needed::*;
-use crate::value::Flag;
-
-impl<'a> Pickable<'a> for Flag {
- fn get_attr(_: &'a PickerArg<'a, Self>) -> PickerArgAttr {
- PickerArgAttr::Flag
- }
-
- fn tag(ctx: TagPhaseContext) -> Vec<usize> {
- FlagMatcher::match_all(ctx.into())
- }
-
- fn pick(raw_strs: &[&str]) -> PickerArgResult<Self> {
- if raw_strs.is_empty() {
- PickerArgResult::Parsed(Flag::Inactive)
- } else {
- PickerArgResult::Parsed(Flag::Active)
- }
- }
-}
diff --git a/mingling_picker/src/builtin/pick_numbers.rs b/mingling_picker/src/builtin/pick_numbers.rs
deleted file mode 100644
index a5ab0a9..0000000
--- a/mingling_picker/src/builtin/pick_numbers.rs
+++ /dev/null
@@ -1,80 +0,0 @@
-use crate::{BoundaryCheck, SinglePickable, pickable_needed::*};
-
-macro_rules! impl_single_pickable_num {
- ($($t:ty),+ $(,)?) => {
- $(impl SinglePickable for $t {
- fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
- match str {
- Some(s) => s.parse::<$t>().map(PickerArgResult::Parsed).unwrap_or(PickerArgResult::NotFound),
- None => PickerArgResult::NotFound,
- }
- }
- })+
- };
-}
-
-/// Returns `true` if `raw` looks like an integer (digits, optional `+`/`-` prefix,
-/// no decimal point or exponent).
-fn is_int_like(raw: &str) -> bool {
- let s = raw.trim();
- let bytes = s.as_bytes();
- if bytes.is_empty() {
- return false;
- }
- let mut i = 0;
- if bytes[0] == b'-' || bytes[0] == b'+' {
- i = 1;
- }
- if i >= bytes.len() {
- return false;
- }
- for &b in &bytes[i..] {
- if !b.is_ascii_digit() {
- return false;
- }
- }
- true
-}
-
-/// Returns `true` if `raw` looks like a float (contains `.`, `e`, or `E`).
-fn is_float_like(raw: &str) -> bool {
- let s = raw.trim();
- s.contains('.') || s.contains('e') || s.contains('E')
-}
-
-impl_single_pickable_num! {
- i8, i16, i32, i64, i128, isize,
- u8, u16, u32, u64, u128, usize,
- f32, f64,
-}
-
-// Integer boundary: only accept strings that look like integers.
-// Float-like strings trigger a boundary.
-macro_rules! impl_boundary_check_int {
- ($($t:ty),+ $(,)?) => {
- $(impl BoundaryCheck for $t {
- fn check_boundary(raw: &str) -> bool {
- !is_int_like(raw)
- }
- })+
- };
-}
-
-impl_boundary_check_int! {
- i8, i16, i32, i64, i128, isize,
- u8, u16, u32, u64, u128, usize,
-}
-
-// Float boundary: only accept strings that look like floats.
-// Integer-like strings trigger a boundary.
-impl BoundaryCheck for f32 {
- fn check_boundary(raw: &str) -> bool {
- !is_float_like(raw) || raw.parse::<f32>().is_err()
- }
-}
-
-impl BoundaryCheck for f64 {
- fn check_boundary(raw: &str) -> bool {
- !is_float_like(raw) || raw.parse::<f64>().is_err()
- }
-}
diff --git a/mingling_picker/src/builtin/pick_string.rs b/mingling_picker/src/builtin/pick_string.rs
deleted file mode 100644
index c96f667..0000000
--- a/mingling_picker/src/builtin/pick_string.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-use crate::PickerArgResult::NotFound;
-use crate::{SinglePickable, pickable_needed::*};
-
-impl SinglePickable for String {
- fn pick_single(str: Option<&str>) -> PickerArgResult<Self> {
- match str {
- Some(str) => PickerArgResult::Parsed(str.to_string()),
- None => NotFound,
- }
- }
-}
diff --git a/mingling_picker/src/corebind.rs b/mingling_picker/src/corebind.rs
deleted file mode 100644
index 3581871..0000000
--- a/mingling_picker/src/corebind.rs
+++ /dev/null
@@ -1,2 +0,0 @@
-mod entry_picker;
-pub use entry_picker::*;
diff --git a/mingling_picker/src/corebind/entry_picker.rs b/mingling_picker/src/corebind/entry_picker.rs
deleted file mode 100644
index 69bc4d8..0000000
--- a/mingling_picker/src/corebind/entry_picker.rs
+++ /dev/null
@@ -1,131 +0,0 @@
-use std::marker::PhantomData;
-
-use mingling_core::{ChainProcess, Groupped, ProgramCollect};
-
-use crate::{Pickable, Picker, PickerArg, PickerArgs, PickerPattern1};
-
-/// Trait for converting Mingling entry types (types that implement `Groupped<R>` and `Into<Vec<String>>`)
-/// into [`Picker`] instances for a given route type `R`.
-///
-/// This trait provides a bridge between entry definitions created with the `mingling` framework
-/// and the picker argument system used for CLI argument parsing and routing.
-///
-/// # Type Parameters
-///
-/// * `'a` — The lifetime of the picker and its references to argument definitions.
-/// * `This` — The program type used for dispatching runtime routes; must implement [`ProgramCollect`].
-/// * `Route` — The route type used for dispatching; must implement [`Groupped<This>`].
-pub trait EntryPicker<'a, This> {
- /// Converts `self` into a [`Picker`] for the given route type `Route`.
- fn to_picker(self) -> Picker<'a, ChainProcess<This>>;
-
- /// Starts building a picker pattern with the first argument.
- ///
- /// Returns a [`PickerPattern1`] that can be further chained with additional
- /// arguments, defaults, routes, and post-processing.
- ///
- /// # Type Parameters
- ///
- /// * `Next` — The nominal type of the first argument; must implement [`Pickable`].
- ///
- /// # Parameters
- ///
- /// * `arg` — The argument definition, typically obtained from [`crate::arg`].
- fn pick<Next>(
- self,
- arg: impl Into<&'a PickerArg<'a, Next>>,
- ) -> PickerPattern1<'a, Next, ChainProcess<This>>
- where
- Self: Sized,
- Next: Pickable<'a> + Default + Sized,
- {
- let picker = Self::to_picker(self);
- Picker::build_pattern1(picker.args, arg.into(), None)
- }
-
- /// Starts building a picker pattern with the first argument, using a default value provider.
- ///
- /// This is a shorthand for calling `.pick(arg).or(func)`.
- ///
- /// # Type Parameters
- ///
- /// * `Next` — The nominal type of the first argument; must implement [`Pickable`].
- ///
- /// # Parameters
- ///
- /// * `arg` — The argument definition, typically obtained from [`crate::arg`].
- /// * `func` — A closure that provides a default value if the arg is not provided by the user.
- fn pick_or<Next, F>(
- self,
- arg: impl Into<&'a PickerArg<'a, Next>>,
- func: F,
- ) -> PickerPattern1<'a, Next, ChainProcess<This>>
- where
- Self: Sized,
- Next: Pickable<'a> + Default + Sized,
- F: FnMut() -> Next + 'static,
- {
- self.pick(arg).or(func)
- }
-
- /// Starts building a picker pattern with the first argument, using a default value.
- ///
- /// This is a shorthand for calling `.pick(arg).or_default()`.
- ///
- /// # Type Parameters
- ///
- /// * `Next` — The nominal type of the first argument; must implement [`Pickable`] and [`Default`].
- ///
- /// # Parameters
- ///
- /// * `arg` — The argument definition, typically obtained from [`crate::arg`].
- fn pick_or_default<Next>(
- self,
- arg: impl Into<&'a PickerArg<'a, Next>>,
- ) -> PickerPattern1<'a, Next, ChainProcess<This>>
- where
- Self: Sized,
- Next: Pickable<'a> + Default + Sized,
- {
- self.pick(arg).or_default()
- }
-
- /// Starts building a picker pattern with the first argument, using a route if the arg is not provided.
- ///
- /// This is a shorthand for calling `.pick(arg).or_route(func)`.
- ///
- /// # Type Parameters
- ///
- /// * `Next` — The nominal type of the first argument; must implement [`Pickable`].
- ///
- /// # Parameters
- ///
- /// * `arg` — The argument definition, typically obtained from [`crate::arg`].
- /// * `func` — A closure that produces a route value if the arg is not provided by the user.
- fn pick_or_route<Next, F>(
- self,
- arg: impl Into<&'a PickerArg<'a, Next>>,
- func: F,
- ) -> PickerPattern1<'a, Next, ChainProcess<This>>
- where
- Self: Sized,
- Next: Pickable<'a> + Default + Sized,
- F: FnMut() -> ChainProcess<This> + 'static,
- {
- self.pick(arg).or_route(func)
- }
-}
-
-impl<'a, This, Bind> EntryPicker<'a, This> for Bind
-where
- This: ProgramCollect<Enum = This>,
- Bind: Groupped<This> + Into<Vec<String>>,
-{
- fn to_picker(self) -> Picker<'a, ChainProcess<This>> {
- let args = self.into();
- Picker {
- route_phantom: PhantomData,
- args: PickerArgs::Owned(args),
- }
- }
-}
diff --git a/mingling_picker/src/infos.rs b/mingling_picker/src/infos.rs
deleted file mode 100644
index d2a0fce..0000000
--- a/mingling_picker/src/infos.rs
+++ /dev/null
@@ -1,446 +0,0 @@
-use crate::{Pickable, PickerArg};
-
-/// Represents the result of parsing or looking up a value.
-///
-/// This enum is generic over the type being parsed. It models three possible outcomes:
-/// - [`Unparsed`](PickerArgResult::Unparsed): The value has not yet been parsed (default).
-/// - [`Parsed`](PickerArgResult::Parsed): The value was successfully parsed into `Type`.
-/// - [`NotFound`](PickerArgResult::NotFound): The requested value could not be found.
-#[derive(Default)]
-pub enum PickerArgResult<Type> {
- /// The value has not yet been parsed (default).
- #[default]
- Unparsed,
-
- /// The value was successfully parsed into `Type`.
- Parsed(Type),
-
- /// The requested value could not be found.
- NotFound,
-}
-
-impl<Type, E> From<Result<Type, E>> for PickerArgResult<Type> {
- /// Converts a `Result<Type, E>` into a `PickerArgResult<Type>`.
- ///
- /// - `Ok(value)` maps to [`Parsed(value)`](PickerArgResult::Parsed).
- /// - `Err(_)` maps to [`NotFound`](PickerArgResult::NotFound).
- fn from(result: Result<Type, E>) -> Self {
- match result {
- Ok(value) => PickerArgResult::Parsed(value),
- Err(_) => PickerArgResult::NotFound,
- }
- }
-}
-
-impl<Type> From<Option<Type>> for PickerArgResult<Type> {
- /// Converts an `Option<Type>` into a `PickerArgResult<Type>`.
- ///
- /// - `Some(value)` maps to [`Parsed(value)`](PickerArgResult::Parsed).
- /// - `None` maps to [`NotFound`](PickerArgResult::NotFound).
- fn from(option: Option<Type>) -> Self {
- match option {
- Some(value) => PickerArgResult::Parsed(value),
- None => PickerArgResult::NotFound,
- }
- }
-}
-
-impl<Type> PickerArgResult<Type> {
- /// Returns `true` if the result is [`Parsed`](PickerArgResult::Parsed).
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling_picker::PickerArgResult;
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::Parsed(42);
- /// assert!(result.is_parsed());
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::NotFound;
- /// assert!(!result.is_parsed());
- /// ```
- pub fn is_parsed(&self) -> bool {
- matches!(self, PickerArgResult::Parsed(_))
- }
-
- /// Returns `true` if the result is [`Parsed`](PickerArgResult::Parsed) or [`NotFound`](PickerArgResult::NotFound).
- /// i.e., the value exists (was either found or not yet parsed).
- /// Typically indicates the value was "found" in some sense.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling_picker::PickerArgResult;
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::Parsed(42);
- /// assert!(result.is_found());
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::NotFound;
- /// assert!(result.is_found());
- /// ```
- pub fn is_found(&self) -> bool {
- matches!(self, PickerArgResult::Parsed(_) | PickerArgResult::NotFound)
- }
-
- /// Returns `true` if the result is [`Unparsed`](PickerArgResult::Unparsed) or [`NotFound`](PickerArgResult::NotFound).
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling_picker::PickerArgResult;
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::Unparsed;
- /// assert!(result.is_err());
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::Parsed(10);
- /// assert!(!result.is_err());
- /// ```
- pub fn is_err(&self) -> bool {
- !matches!(self, PickerArgResult::Parsed(_))
- }
-
- /// Returns `Some(&Type)` if [`Parsed`](PickerArgResult::Parsed), otherwise `None`.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling_picker::PickerArgResult;
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::Parsed(42);
- /// assert_eq!(result.parsed(), Some(&42));
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::NotFound;
- /// assert_eq!(result.parsed(), None);
- /// ```
- pub fn parsed(&self) -> Option<&Type> {
- if let PickerArgResult::Parsed(value) = self {
- Some(value)
- } else {
- None
- }
- }
-
- /// Returns the contained [`Parsed`](PickerArgResult::Parsed) value or panics with a given message.
- ///
- /// # Panics
- /// Panics if the value is not [`Parsed`](PickerArgResult::Parsed), with a message including the provided `msg`.
- ///
- /// # Examples
- ///
- /// ```should_panic
- /// use mingling_picker::PickerArgResult;
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::NotFound;
- /// result.expect("expected a parsed value");
- /// ```
- pub fn expect(self, msg: &str) -> Type {
- match self {
- PickerArgResult::Parsed(value) => value,
- _ => panic!("{}", msg),
- }
- }
-
- /// Returns the contained [`Parsed`](PickerArgResult::Parsed) value or panics.
- ///
- /// # Panics
- /// Panics if the value is not [`Parsed`](PickerArgResult::Parsed).
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling_picker::PickerArgResult;
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::Parsed(42);
- /// assert_eq!(result.unwrap(), 42);
- /// ```
- ///
- /// ```should_panic
- /// use mingling_picker::PickerArgResult;
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::NotFound;
- /// result.unwrap();
- /// ```
- pub fn unwrap(self) -> Type {
- match self {
- PickerArgResult::Parsed(value) => value,
- PickerArgResult::Unparsed => {
- panic!("called `PickerArgResult::unwrap()` on an `Unparsed` value")
- }
- PickerArgResult::NotFound => {
- panic!("called `PickerArgResult::unwrap()` on a `NotFound` value")
- }
- }
- }
-
- /// Returns the contained [`Parsed`](PickerArgResult::Parsed) value or a provided `default`.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling_picker::PickerArgResult;
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::Parsed(42);
- /// assert_eq!(result.unwrap_or(0), 42);
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::NotFound;
- /// assert_eq!(result.unwrap_or(0), 0);
- /// ```
- pub fn unwrap_or(self, default: Type) -> Type {
- match self {
- PickerArgResult::Parsed(value) => value,
- _ => default,
- }
- }
-
- /// Returns the contained [`Parsed`](PickerArgResult::Parsed) value or computes it from a closure.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling_picker::PickerArgResult;
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::Parsed(42);
- /// assert_eq!(result.unwrap_or_else(|| 0), 42);
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::NotFound;
- /// assert_eq!(result.unwrap_or_else(|| 0), 0);
- /// ```
- pub fn unwrap_or_else<F: FnOnce() -> Type>(self, f: F) -> Type {
- match self {
- PickerArgResult::Parsed(value) => value,
- _ => f(),
- }
- }
-
- /// Returns the contained [`Parsed`](PickerArgResult::Parsed) value or the default value of `Type`.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling_picker::PickerArgResult;
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::Parsed(42);
- /// assert_eq!(result.unwrap_or_default(), 42);
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::NotFound;
- /// assert_eq!(result.unwrap_or_default(), 0);
- /// ```
- pub fn unwrap_or_default(self) -> Type
- where
- Type: Default,
- {
- match self {
- PickerArgResult::Parsed(value) => value,
- _ => Type::default(),
- }
- }
-
- /// Converts `PickerArgResult<Type>` into `Option<Type>`.
- ///
- /// Returns `Some(Type)` if [`Parsed`](PickerArgResult::Parsed), otherwise `None`.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling_picker::PickerArgResult;
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::Parsed(42);
- /// assert_eq!(result.to_option(), Some(42));
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::NotFound;
- /// assert_eq!(result.to_option(), None);
- ///
- /// let result: PickerArgResult<i32> = PickerArgResult::Unparsed;
- /// assert_eq!(result.to_option(), None);
- /// ```
- pub fn to_option(self) -> Option<Type> {
- match self {
- PickerArgResult::Parsed(value) => Some(value),
- _ => None,
- }
- }
-}
-
-/// Represents metadata about a command-line argument or flag.
-///
-/// This struct stores all relevant information about a tag/argument that can be used
-/// for parsing command-line inputs. It includes the short form (e.g., `-n`), long form
-/// (e.g., `--name`), aliases, and various flags that control parsing behavior.
-pub struct PickerArgInfo<'a> {
- /// The short form of the tag, e.g. `'n'` for `-n`.
- pub short: Option<char>,
- /// The long form of the tag, e.g. `"name"` for `--name`.
- pub long: Option<&'a str>,
- /// Alternative names for the tag, e.g. `["-N", "--nickname"]`.
- pub alias: Option<Vec<&'a str>>,
- /// Whether this tag is a positional argument (no `-` or `--` prefix).
- pub positional: bool,
- /// Whether this tag is optional or required.
- pub optional: bool,
- /// Whether this tag can accept multiple values.
- pub multi: bool,
- /// Whether this tag participates in parsing after a `--` separator.
- pub is_flag: bool,
-}
-
-impl<'a, T> From<PickerArg<'a, T>> for PickerArgInfo<'a>
-where
- T: Pickable<'a>,
-{
- fn from(value: PickerArg<'a, T>) -> Self {
- let (long, alias) = match value.full.len() {
- 0 => (None, None),
- _ => {
- let long = Some(value.full[0]);
- let alias = if value.full.len() > 1 {
- Some(value.full[1..].to_vec())
- } else {
- None
- };
- (long, alias)
- }
- };
-
- Self {
- short: value.short,
- long,
- alias,
- positional: value.positional,
- optional: false,
- multi: false,
- is_flag: false,
- }
- }
-}
-
-impl<'a, T: Pickable<'a>> From<&'a PickerArg<'a, T>> for PickerArgInfo<'a> {
- fn from(value: &'a PickerArg<'a, T>) -> Self {
- let (long, alias) = match value.full.len() {
- 0 => (None, None),
- _ => {
- let long = Some(value.full[0]);
- let alias = if value.full.len() > 1 {
- Some(value.full[1..].to_vec())
- } else {
- None
- };
- (long, alias)
- }
- };
-
- Self {
- short: value.short,
- long,
- alias,
- positional: value.positional,
- optional: false,
- multi: false,
- is_flag: false,
- }
- }
-}
-
-impl<'a> PickerArgInfo<'a> {
- /// Create a new `PickerTag` with default values.
- pub fn new() -> Self {
- Self {
- short: None,
- long: None,
- alias: None,
- positional: false,
- optional: false,
- multi: false,
- is_flag: false,
- }
- }
-
- /// Set the short flag (e.g., `'n'` for `-n`).
- pub fn with_short(mut self, short: char) -> Self {
- self.short = Some(short);
- self
- }
-
- /// Set the long flag (e.g., `"name"` for `--name`).
- pub fn with_long(mut self, long: &'a str) -> Self {
- self.long = Some(long);
- self
- }
-
- /// Set aliases for the tag.
- pub fn with_alias(mut self, alias: Vec<&'a str>) -> Self {
- self.alias = Some(alias);
- self
- }
-
- /// Mark the tag as positional.
- pub fn with_positional(mut self, positional: bool) -> Self {
- self.positional = positional;
- self
- }
-
- /// Mark the tag as optional.
- pub fn with_optional(mut self, optional: bool) -> Self {
- self.optional = optional;
- self
- }
-
- /// Mark the tag as multi-value.
- pub fn with_multi(mut self, multi: bool) -> Self {
- self.multi = multi;
- self
- }
-
- /// Mark the tag as a flag that participates in parsing after `--`.
- pub fn with_is_flag(mut self, is_flag: bool) -> Self {
- self.is_flag = is_flag;
- self
- }
-
- /// Set the short flag (e.g., `'n'` for `-n`).
- pub fn set_short(&mut self, short: char) -> &mut Self {
- self.short = Some(short);
- self
- }
-
- /// Set the long flag (e.g., `"name"` for `--name`).
- pub fn set_long(&mut self, long: &'a str) -> &mut Self {
- self.long = Some(long);
- self
- }
-
- /// Set aliases for the tag.
- pub fn set_alias(&mut self, alias: Vec<&'a str>) -> &mut Self {
- self.alias = Some(alias);
- self
- }
-
- /// Set whether this tag is positional.
- pub fn set_positional(&mut self, positional: bool) -> &mut Self {
- self.positional = positional;
- self
- }
-
- /// Set whether this tag is optional.
- pub fn set_optional(&mut self, optional: bool) -> &mut Self {
- self.optional = optional;
- self
- }
-
- /// Set whether this tag accepts multiple values.
- pub fn set_multi(&mut self, multi: bool) -> &mut Self {
- self.multi = multi;
- self
- }
-
- /// Set whether this tag participates in parsing after a `--` separator.
- pub fn set_is_flag(&mut self, is_flag: bool) -> &mut Self {
- self.is_flag = is_flag;
- self
- }
-}
-
-impl<'a> Default for PickerArgInfo<'a> {
- fn default() -> Self {
- Self::new()
- }
-}
diff --git a/mingling_picker/src/lib.rs b/mingling_picker/src/lib.rs
deleted file mode 100644
index deb266e..0000000
--- a/mingling_picker/src/lib.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-#![doc = include_str!("../README.md")]
-
-mod builtin;
-
-mod picker;
-pub use picker::*;
-
-mod pickable;
-pub use pickable::*;
-
-mod arg;
-pub use arg::*;
-
-mod infos;
-pub use infos::*;
-
-/// Provides the specific parsing logic for command-line arguments and common utilities,
-/// as well as customization of command-line argument styles.
-pub mod parselib;
-
-/// Parser-provided parseable command-line types
-pub mod value;
-
-/// The prelude module, which re-exports the most commonly used traits and types.
-///
-/// This module is intended to be imported with a wildcard import:
-///
-/// ```
-/// use mingling_picker::prelude::*;
-/// ```
-pub mod prelude {
- pub use crate::macros::arg;
-
- #[cfg(not(feature = "mingling_support"))]
- pub use crate::IntoPicker;
-
- #[cfg(feature = "mingling_support")]
- pub use crate::corebind::EntryPicker;
-}
-
-/// Re-export of the `mingling_picker_macros` crate
-pub mod macros {
- pub use mingling_picker_macros::arg;
-}
-
-/// Provides the types necessary for implementing the `Pickable` trait
-pub mod pickable_needed {
- pub use crate::{Pickable, PickerArg, PickerArgAttr, PickerArgResult, TagPhaseContext};
-}
-
-/// Provides the types necessary for implementing the `Matcher` trait
-pub mod matcher_needed {
- pub use crate::PickerArgInfo;
- pub use crate::parselib::{MaskedArg, Matcher, ParserStyle};
-}
-
-#[cfg(feature = "mingling_support")]
-mod corebind;
-
-#[allow(unused_imports)]
-#[cfg(feature = "mingling_support")]
-pub use corebind::*;
diff --git a/mingling_picker/src/parselib.rs b/mingling_picker/src/parselib.rs
deleted file mode 100644
index 7fbd606..0000000
--- a/mingling_picker/src/parselib.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-mod flag_matcher;
-pub use flag_matcher::*;
-
-mod arg_matcher;
-pub use arg_matcher::*;
-
-mod multi_arg_matcher;
-pub use multi_arg_matcher::*;
-
-mod pos_matcher;
-pub use pos_matcher::*;
-
-mod single_matcher;
-pub use single_matcher::*;
-
-mod style;
-pub use style::*;
-
-mod utils;
-pub use utils::*;
-
-use crate::{PickerArgInfo, PickerArgs};
-
-/// Represents a single argument with its original raw string and index.
-///
-/// This is used during pattern matching to provide context about
-/// which argument is being processed.
-#[derive(Clone, Copy, PartialEq, Eq)]
-pub struct MaskedArg<'a> {
- /// The raw string value of the argument.
- pub raw: &'a str,
- /// The original index of the argument in the full argument list.
- pub raw_idx: usize,
-}
-
-/// Trait for defining matching logic against masked arguments.
-///
-/// Implementors can define custom strategies for matching one or all
-/// arguments that pass through a mask filter.
-pub trait Matcher {
- /// Called when only one match is needed.
- ///
- /// Returns the index of the first matched argument, or `None` if no match.
- fn on_match_one(
- args: &[MaskedArg],
- style: &ParserStyle,
- arg_info: &PickerArgInfo,
- ) -> Option<usize>;
-
- /// Called when all matches are needed.
- ///
- /// Returns a vector of indices of all matched arguments.
- fn on_match_all(
- args: &[MaskedArg],
- style: &ParserStyle,
- arg_info: &PickerArgInfo,
- ) -> Vec<usize>;
-
- /// Convenience method that builds masked arguments from `PickerArgs` and a mask,
- /// then calls `on_match_one`.
- fn match_one<'a>(ctx: MatcherContext<'a>) -> Option<usize> {
- let masked_args = build_masked_args(ctx.args, ctx.mask);
- Self::on_match_one(masked_args.as_slice(), ctx.style, ctx.arg_info)
- }
-
- /// Convenience method that builds masked arguments from `PickerArgs` and a mask,
- /// then calls `on_match_all`.
- fn match_all<'a>(ctx: MatcherContext<'a>) -> Vec<usize> {
- let masked_args = build_masked_args(ctx.args, ctx.mask);
- Self::on_match_all(masked_args.as_slice(), ctx.style, ctx.arg_info)
- }
-}
-
-/// Context for matcher operations
-///
-/// This struct bundles together the key pieces of data needed during matching:
-/// - `args`: The full set of parsed arguments.
-/// - `mask`: A byte mask indicating which arguments are currently active (non-zero = active).
-/// - `style`: The parsing style configuration.
-pub struct MatcherContext<'a> {
- /// The full set of parsed arguments.
- pub args: &'a PickerArgs<'a>,
-
- /// A byte mask where non-zero values indicate the argument at that position is active/should be matched.
- pub mask: &'a [u8],
-
- /// The parsing style configuration.
- pub style: &'a ParserStyle<'a>,
-
- /// Metadata about the command-line argument/flag being processed.
- ///
- /// Contains information such as short form (`-n`), long form (`--name`),
- /// aliases, and parsing flags (positional, optional, multi, is_flag).
- /// Used by matchers to make decisions based on argument characteristics.
- pub arg_info: &'a PickerArgInfo<'a>,
-}
-
-impl<'a> From<&'a crate::TagPhaseContext<'a>> for MatcherContext<'a> {
- fn from(ctx: &'a crate::TagPhaseContext<'a>) -> Self {
- MatcherContext {
- args: ctx.args,
- mask: ctx.mask,
- style: ParserStyle::global_style(),
- arg_info: ctx.arg_info,
- }
- }
-}
-
-impl<'a> From<crate::TagPhaseContext<'a>> for MatcherContext<'a> {
- fn from(ctx: crate::TagPhaseContext<'a>) -> Self {
- MatcherContext {
- args: ctx.args,
- mask: ctx.mask,
- style: ParserStyle::global_style(),
- arg_info: ctx.arg_info,
- }
- }
-}
-
-#[inline(always)]
-fn is_masked(mask: &[u8], idx: usize) -> bool {
- idx < mask.len() && mask[idx] != 0
-}
-
-#[inline(always)]
-fn build_masked_args<'a>(args: &'a PickerArgs, mask: &'a [u8]) -> Vec<MaskedArg<'a>> {
- let mut cidx = 0;
- args.iter()
- .filter_map(|r| {
- let idx = cidx;
- cidx += 1;
- // Include args where mask is 0 (available/not yet claimed).
- // mask[i] = 0 means available; mask[i] != 0 means already claimed.
- if !is_masked(mask, idx) {
- Some(MaskedArg {
- raw: r,
- raw_idx: idx,
- })
- } else {
- None
- }
- })
- .collect()
-}
diff --git a/mingling_picker/src/parselib/arg_matcher.rs b/mingling_picker/src/parselib/arg_matcher.rs
deleted file mode 100644
index 38bb9cc..0000000
--- a/mingling_picker/src/parselib/arg_matcher.rs
+++ /dev/null
@@ -1,142 +0,0 @@
-use crate::{
- matcher_needed::*,
- parselib::{build_possible_flags, seek_end_of_options},
-};
-
-/// `ArgMatcher` is used for parameters that carry a single value.
-///
-/// It handles two scenarios:
-///
-/// **Named** — `--name Alice` or `--name=Alice`.
-/// Each flag occurrence consumes **one** following argument as its value,
-/// regardless of what it is (even if it looks like a flag).
-/// This ensures the mask correctly claims the value slot; validation is
-/// the `Pickable`'s responsibility.
-///
-/// **Positional** — no flag prefix, matched by position.
-///
-/// # Examples
-///
-/// | Input | `on_match_one` | `on_match_all` |
-/// |-------|----------------|----------------|
-/// | `--name Alice` | `[0, 1]` (via Pickable tag) | `[0, 1]` |
-/// | `--name=Alice` | `[0]` | `[0]` |
-/// | `--val a --val b` | `[0, 1]` | `[0, 1, 2, 3]` |
-///
-/// Args after `--` are ignored.
-pub struct ArgMatcher;
-
-impl ArgMatcher {
- /// Check whether `raw` matches `flag_str`, optionally with an inline value
- /// separated by the style's value separator (`=` for Unix, `:` for PowerShell).
- #[inline(always)]
- fn matches(raw: &str, flag_str: &str, case_sensitive: bool, sep: char) -> bool {
- let eq_match =
- |r: &str, f: &str| r.len() > f.len() && r.as_bytes().get(f.len()) == Some(&(sep as u8));
-
- if case_sensitive {
- raw == flag_str || (raw.starts_with(flag_str) && eq_match(raw, flag_str))
- } else {
- raw.eq_ignore_ascii_case(flag_str)
- || (raw.len() > flag_str.len()
- && raw[..flag_str.len()].eq_ignore_ascii_case(flag_str)
- && raw.as_bytes()[flag_str.len()] == sep as u8)
- }
- }
-
- /// Check whether the argument contains its value inline via the style's
- /// value separator (eq mode), so no extra mask slot is needed.
- #[inline(always)]
- fn is_inline_value(raw: &str, flag_str: &str, sep: char) -> bool {
- raw.len() > flag_str.len() && raw.as_bytes().get(flag_str.len()) == Some(&(sep as u8))
- }
-}
-
-impl Matcher for ArgMatcher {
- fn on_match_one(
- args: &[MaskedArg],
- style: &ParserStyle,
- arg_info: &PickerArgInfo,
- ) -> Option<usize> {
- if arg_info.positional {
- return args.first().map(|a| a.raw_idx);
- }
-
- let possible_flags = build_possible_flags(style, arg_info);
- let end = seek_end_of_options(args, style);
- let sep = style.value_separator;
-
- for arg in args {
- if end.is_some_and(|e| arg.raw_idx >= e) {
- break;
- }
-
- let matched = possible_flags
- .iter()
- .any(|f| Self::matches(arg.raw, f, style.case_sensitive, sep));
- if matched {
- return Some(arg.raw_idx);
- }
- }
-
- None
- }
-
- fn on_match_all(
- args: &[MaskedArg],
- style: &ParserStyle,
- arg_info: &PickerArgInfo,
- ) -> Vec<usize> {
- if arg_info.positional {
- let end = seek_end_of_options(args, style);
- return args
- .iter()
- .take_while(|a| end.is_none_or(|e| a.raw_idx < e))
- .map(|a| a.raw_idx)
- .collect();
- }
-
- let possible_flags = build_possible_flags(style, arg_info);
- let end = seek_end_of_options(args, style);
- let sep = style.value_separator;
-
- let mut result = Vec::new();
- let mut i = 0;
- while i < args.len() {
- if end.is_some_and(|e| args[i].raw_idx >= e) {
- break;
- }
-
- let matched = possible_flags
- .iter()
- .any(|f| Self::matches(args[i].raw, f, style.case_sensitive, sep));
-
- if matched {
- let flag_str = possible_flags
- .iter()
- .find(|f| Self::matches(args[i].raw, f, style.case_sensitive, sep))
- .expect("already matched");
-
- result.push(args[i].raw_idx);
-
- if !Self::is_inline_value(args[i].raw, flag_str, sep) {
- if i + 1 < args.len()
- // Don't consume `--` (end-of-options marker) as a value.
- && end.is_none_or(|e| args[i + 1].raw_idx < e)
- {
- result.push(args[i + 1].raw_idx);
- i += 2;
- continue;
- }
- i += 1;
- continue;
- }
- i += 1;
- continue;
- }
- i += 1;
- }
-
- result
- }
-}
diff --git a/mingling_picker/src/parselib/flag_matcher.rs b/mingling_picker/src/parselib/flag_matcher.rs
deleted file mode 100644
index e93d35a..0000000
--- a/mingling_picker/src/parselib/flag_matcher.rs
+++ /dev/null
@@ -1,82 +0,0 @@
-use crate::{
- matcher_needed::*,
- parselib::{build_possible_flags, get_seeked_first, multi_seek_eq, seek_end_of_options},
-};
-
-/// `FlagMatcher` is used to match flags in command-line arguments.
-///
-/// Flags typically start with `-` or `--` (e.g., `-h`, `--help`),
-/// and do not carry additional values. This matcher is responsible for finding
-/// these flags in the argument list, taking into account that flags after `--`
-/// (end-of-options marker) should not be matched.
-pub struct FlagMatcher;
-
-impl Matcher for FlagMatcher {
- fn on_match_one(
- args: &[MaskedArg],
- style: &ParserStyle,
- arg_info: &PickerArgInfo,
- ) -> Option<usize> {
- let possible_flags = build_possible_flags(style, arg_info);
- let flag_refs: Vec<&str> = possible_flags.iter().map(|s| s.as_str()).collect();
- let end_of_options = seek_end_of_options(args, style);
-
- let result = get_seeked_first(multi_seek_eq(args, &flag_refs, style.case_sensitive));
-
- match (end_of_options, result) {
- (Some(end), Some(current)) if current > end => None,
- _ => result,
- }
- }
-
- fn on_match_all(
- args: &[MaskedArg],
- style: &ParserStyle,
- arg_info: &PickerArgInfo,
- ) -> Vec<usize> {
- let possible_flags = build_possible_flags(style, arg_info);
- single_pass_match_all(args, style, &possible_flags)
- }
-}
-
-/// Single-pass match: finds the `--` marker and matching flags in one iteration.
-fn single_pass_match_all(
- args: &[MaskedArg],
- style: &ParserStyle,
- possible_flags: &[String],
-) -> Vec<usize> {
- let flag_refs: Vec<&str> = possible_flags.iter().map(|s| s.as_str()).collect();
- let eoo = style.end_of_options;
- let case_sensitive = style.case_sensitive;
-
- let mut end_pos: Option<usize> = None;
- let mut matches: Vec<usize> = Vec::new();
-
- for arg in args {
- if end_pos.is_none() {
- let is_eoo = if case_sensitive {
- arg.raw == eoo
- } else {
- arg.raw.eq_ignore_ascii_case(eoo)
- };
- if is_eoo {
- end_pos = Some(arg.raw_idx);
- continue;
- }
- }
-
- // Only match flags before the end-of-options marker.
- if end_pos.is_none() {
- let matched = if case_sensitive {
- flag_refs.contains(&arg.raw)
- } else {
- flag_refs.iter().any(|s| arg.raw.eq_ignore_ascii_case(s))
- };
- if matched {
- matches.push(arg.raw_idx);
- }
- }
- }
-
- matches
-}
diff --git a/mingling_picker/src/parselib/multi_arg_matcher.rs b/mingling_picker/src/parselib/multi_arg_matcher.rs
deleted file mode 100644
index 748b1be..0000000
--- a/mingling_picker/src/parselib/multi_arg_matcher.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-use crate::{
- matcher_needed::*,
- parselib::{build_possible_flags, seek_end_of_options},
-};
-
-/// `MultiArgMatcher` matches a named flag and **all** consecutive arguments
-/// that follow it, stopping at the next flag, the `--` marker, or the end
-/// of the argument list.
-///
-/// This is the tag implementation for `Multi` and `GreedyMulti` types
-/// such as `Vec<String>` (`--files a.txt b.txt`).
-///
-/// # Behavior
-///
-/// | Input | `on_match_all` |
-/// |-------|----------------|
-/// | `--val a b --val d e` | `[0, 1, 2, 5, 6]` (two groups) |
-/// | `--val=1 2` | `[0, 1]` (eq mode + one extra value) |
-///
-/// Args after `--` are ignored.
-pub struct MultiArgMatcher;
-
-impl Matcher for MultiArgMatcher {
- fn on_match_one(
- args: &[MaskedArg],
- style: &ParserStyle,
- arg_info: &PickerArgInfo,
- ) -> Option<usize> {
- if arg_info.positional {
- return args.first().map(|a| a.raw_idx);
- }
-
- let possible_flags = build_possible_flags(style, arg_info);
- let end = seek_end_of_options(args, style);
- let sep = style.value_separator;
-
- for arg in args {
- if end.is_some_and(|e| arg.raw_idx >= e) {
- break;
- }
- let matched = possible_flags
- .iter()
- .any(|f| Self::flag_match(arg.raw, f, style.case_sensitive, sep));
- if matched {
- return Some(arg.raw_idx);
- }
- }
- None
- }
-
- fn on_match_all(
- args: &[MaskedArg],
- style: &ParserStyle,
- arg_info: &PickerArgInfo,
- ) -> Vec<usize> {
- if arg_info.positional {
- let end = seek_end_of_options(args, style);
- return args
- .iter()
- .take_while(|a| end.is_none_or(|e| a.raw_idx < e))
- .map(|a| a.raw_idx)
- .collect();
- }
-
- let possible_flags = build_possible_flags(style, arg_info);
- let end = seek_end_of_options(args, style);
- let sep = style.value_separator;
- let is_flag =
- |raw: &str| raw.starts_with(style.long_prefix) || raw.starts_with(style.short_prefix);
- let is_our_flag = |raw: &str| {
- possible_flags
- .iter()
- .any(|f| Self::flag_match(raw, f, style.case_sensitive, sep))
- };
-
- let mut result = Vec::new();
- let mut i = 0;
- while i < args.len() {
- if end.is_some_and(|e| args[i].raw_idx >= e) {
- break;
- }
-
- let matched = is_our_flag(args[i].raw);
-
- if matched {
- result.push(args[i].raw_idx);
-
- if Self::is_eq_match(args[i].raw, &possible_flags, style.case_sensitive, sep) {
- i += 1;
- while i < args.len()
- && end.is_none_or(|e| args[i].raw_idx < e)
- && !is_flag(args[i].raw)
- {
- result.push(args[i].raw_idx);
- i += 1;
- }
- continue;
- }
-
- i += 1;
- while i < args.len()
- && end.is_none_or(|e| args[i].raw_idx < e)
- && !is_flag(args[i].raw)
- {
- result.push(args[i].raw_idx);
- i += 1;
- }
- continue;
- }
- i += 1;
- }
-
- result
- }
-}
-
-impl MultiArgMatcher {
- #[inline(always)]
- fn flag_match(raw: &str, flag_str: &str, case_sensitive: bool, sep: char) -> bool {
- let eq =
- |r: &str, f: &str| r.len() > f.len() && r.as_bytes().get(f.len()) == Some(&(sep as u8));
-
- if case_sensitive {
- raw == flag_str || (raw.starts_with(flag_str) && eq(raw, flag_str))
- } else {
- raw.eq_ignore_ascii_case(flag_str)
- || (raw.len() > flag_str.len()
- && raw[..flag_str.len()].eq_ignore_ascii_case(flag_str)
- && raw.as_bytes()[flag_str.len()] == sep as u8)
- }
- }
-
- #[inline(always)]
- fn is_eq_match(raw: &str, flags: &[String], case_sensitive: bool, sep: char) -> bool {
- flags.iter().any(|f| {
- Self::flag_match(raw, f, case_sensitive, sep)
- && raw.len() > f.len()
- && raw.as_bytes().get(f.len()) == Some(&(sep as u8))
- })
- }
-}
diff --git a/mingling_picker/src/parselib/pos_matcher.rs b/mingling_picker/src/parselib/pos_matcher.rs
deleted file mode 100644
index 279e01e..0000000
--- a/mingling_picker/src/parselib/pos_matcher.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-use crate::{matcher_needed::*, parselib::seek_end_of_options};
-
-/// `PositionalMatcher` matches positional arguments — values not associated
-/// with any named flag.
-///
-/// # Rules
-///
-/// * Before `--`: skips any argument that starts with the style's long or short
-/// prefix (those belong to named matchers).
-/// * After `--`: takes **everything** — the `--` marker signals that all
-/// remaining values are positional, even if they look like flags.
-/// * Runs at the lowest priority (see [`PickerArgAttr::Positional`](crate::PickerArgAttr::Positional)).
-pub struct PositionalMatcher;
-
-impl PositionalMatcher {
- /// Check whether `raw` looks like a named flag (starts with a prefix).
- #[inline(always)]
- fn is_flag_like(raw: &str, style: &ParserStyle) -> bool {
- raw.starts_with(style.long_prefix) || raw.starts_with(style.short_prefix)
- }
-}
-
-impl Matcher for PositionalMatcher {
- fn on_match_one(
- args: &[MaskedArg],
- style: &ParserStyle,
- _arg_info: &PickerArgInfo,
- ) -> Option<usize> {
- let end = seek_end_of_options(args, style);
-
- for arg in args {
- if end.is_some_and(|e| arg.raw_idx == e) {
- // Hit `--`: everything from here on is positional,
- // including the first arg after `--`.
- continue;
- }
- if end.is_some_and(|e| arg.raw_idx > e) {
- // After `--`: accept everything.
- return Some(arg.raw_idx);
- }
- // Before `--`: skip flag-like args.
- if !Self::is_flag_like(arg.raw, style) {
- return Some(arg.raw_idx);
- }
- }
-
- None
- }
-
- fn on_match_all(
- args: &[MaskedArg],
- style: &ParserStyle,
- _arg_info: &PickerArgInfo,
- ) -> Vec<usize> {
- let end = seek_end_of_options(args, style);
- let mut after_end = false;
- let mut result = Vec::new();
-
- for arg in args {
- if end.is_some_and(|e| arg.raw_idx == e) {
- after_end = true;
- continue;
- }
- if after_end || !Self::is_flag_like(arg.raw, style) {
- result.push(arg.raw_idx);
- }
- }
-
- result
- }
-}
diff --git a/mingling_picker/src/parselib/single_matcher.rs b/mingling_picker/src/parselib/single_matcher.rs
deleted file mode 100644
index 25c4741..0000000
--- a/mingling_picker/src/parselib/single_matcher.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-use crate::TagPhaseContext;
-use crate::parselib::{ArgMatcher, Matcher, ParserStyle, PositionalMatcher};
-
-/// `SingleMatcher` is a composite matcher for single-value parameters.
-///
-/// It delegates to [`PositionalMatcher`] for positional args and
-/// [`ArgMatcher`] for named args, adding a guard: if a named flag
-/// captures only itself with no inline value (eq mode), the result
-/// is cleared so that [`Pickable::pick`](crate::Pickable::pick) receives `[]` → `NotFound`.
-///
-/// This is the standard tag implementation for all `Single`-type
-/// `Pickable` implementations (e.g., `String`, `i32`, `u64`).
-pub struct SingleMatcher;
-
-impl SingleMatcher {
- /// Match a single positional value or a named flag+value pair.
- ///
- /// For named args, only complete pairs (flag + value) are kept.
- /// Flag occurrences without a following value or inline separator
- /// are dropped so they remain available for other matchers.
- #[inline(always)]
- pub fn tag(ctx: TagPhaseContext) -> Vec<usize> {
- if ctx.arg_info.positional {
- PositionalMatcher::match_one(ctx.into())
- .map(|i| vec![i])
- .unwrap_or_default()
- } else {
- let args = ctx.args;
- let positions = ArgMatcher::match_all(ctx.into());
- let sep = ParserStyle::global_style().value_separator;
-
- // Walk pairs: [flag, value, flag, value, ...]
- // Drop any flag that has no following value and no inline separator.
- let mut i = 0;
- let mut result = Vec::with_capacity(positions.len());
- while i < positions.len() {
- let flag_idx = positions[i];
- if let Some(raw) = args.get(flag_idx)
- && raw.contains(sep)
- {
- // Eq mode: value is inline, keep just the flag.
- result.push(flag_idx);
- i += 1;
- continue;
- }
- if i + 1 < positions.len() {
- // Pair: flag + value.
- result.push(flag_idx);
- result.push(positions[i + 1]);
- i += 2;
- } else {
- // Flag without value — drop it.
- i += 1;
- }
- }
- result
- }
- }
-}
diff --git a/mingling_picker/src/parselib/style.rs b/mingling_picker/src/parselib/style.rs
deleted file mode 100644
index dd01125..0000000
--- a/mingling_picker/src/parselib/style.rs
+++ /dev/null
@@ -1,258 +0,0 @@
-use std::sync::OnceLock;
-use std::sync::atomic::{AtomicBool, Ordering};
-
-use crate::parselib::ParserStyleNamingCase::{Kebab, Pascal};
-
-/// Defines the style of command-line argument parsing (prefixes, separators, etc.).
-#[derive(Clone, Copy, PartialEq, Eq)]
-pub struct ParserStyle<'a> {
- /// End-of-options marker (e.g., `--`)
- pub end_of_options: &'a str,
-
- /// Prefix for long options (e.g., `--` or `/`)
- pub long_prefix: &'a str,
-
- /// Prefix for short options (e.g., `-` or `/`)
- pub short_prefix: &'a str,
-
- /// Prefix for combined short flags (e.g., `-abc`)
- pub combine_prefix: &'a str,
-
- /// Separator between name and value (e.g., `=` or `:`)
- pub value_separator: char,
-
- /// Whether option names are case-sensitive
- pub case_sensitive: bool,
-
- /// Whether combining short flags is allowed (e.g., `-abc` for `-a -b -c`)
- pub allow_combine: bool,
-
- /// Naming case
- pub naming_case: ParserStyleNamingCase,
-}
-
-impl<'a> ParserStyle<'a> {
- /// Formats a flag (short or long) into a full command-line option string.
- ///
- /// This method takes any type that can be converted into a `FlagStr` and produces
- /// a complete option string by prepending the appropriate prefix.
- ///
- /// # Examples
- ///
- /// ```
- /// # use mingling_picker::parselib::{ParserStyle, FlagStr, UNIX_STYLE};
- /// let style = &UNIX_STYLE;
- ///
- /// assert_eq!(style.flag_string('v'), "-v");
- /// assert_eq!(style.flag_string("verbose"), "--verbose");
- /// ```
- ///
- /// # Parameters
- ///
- /// * `flag` - A value that can be converted to `FlagStr`, either a `char` for short flags
- /// or a `&str` for long flags.
- ///
- /// # Returns
- ///
- /// A `String` with the prefix and the flag name combined.
- #[must_use]
- #[inline(always)]
- pub fn flag_string<F>(&self, flag: F) -> String
- where
- F: Into<FlagStr<'a>>,
- {
- match flag.into() {
- FlagStr::Short(short) => format!("{}{}", self.short_prefix, short),
- FlagStr::Long(long) => format!("{}{}", self.long_prefix, long),
- }
- }
-}
-
-/// Represents a flag name for command-line argument parsing.
-///
-/// This enum can hold either a short flag (a single character, e.g., `'v'` for `-v`)
-/// or a long flag (a string, e.g., `"verbose"` for `--verbose`).
-///
-/// # Examples
-///
-/// ```
-/// use mingling_picker::parselib::FlagStr;
-///
-/// let short: FlagStr = 'v'.into();
-/// let long: FlagStr = "verbose".into();
-/// ```
-pub enum FlagStr<'a> {
- /// A short flag represented by a single character.
- Short(char),
- /// A long flag represented by a string slice.
- Long(&'a str),
-}
-
-impl<'a> From<char> for FlagStr<'a> {
- /// Converts a single character into a `FlagStr::Short`.
- fn from(c: char) -> Self {
- FlagStr::Short(c)
- }
-}
-
-impl<'a> From<&'a str> for FlagStr<'a> {
- /// Converts a string slice into a `FlagStr::Long`.
- fn from(s: &'a str) -> Self {
- FlagStr::Long(s)
- }
-}
-
-impl<'a> From<&'a String> for FlagStr<'a> {
- /// Converts a reference to a `String` into a `FlagStr::Long`.
- fn from(s: &'a String) -> Self {
- FlagStr::Long(s.as_str())
- }
-}
-
-/// Defines the naming convention for command-line option names.
-///
-/// Each variant represents a different case format that can be applied
-/// to option names (e.g., long option names) during parsing or generation.
-///
-/// # Examples
-///
-/// ```
-/// # use mingling_picker::IntoPicker;
-/// use mingling_picker::parselib::ParserStyleNamingCase;
-///
-/// let case = ParserStyleNamingCase::Kebab;
-/// assert_eq!(
-/// case.convert("brew_coffee".to_string()),
-/// "brew-coffee".to_string()
-/// );
-/// ```
-#[repr(u8)]
-#[derive(Default, Clone, Copy, PartialEq, Eq)]
-pub enum ParserStyleNamingCase {
- /// snake_case format: words are separated by underscores, all lowercase.
- ///
- /// Example: `brew_coffee`
- #[default]
- Snake,
- /// camelCase format: first word is lowercase, subsequent words are capitalized.
- ///
- /// Example: `brewCoffee`
- Camel,
- /// PascalCase format: every word starts with an uppercase letter.
- ///
- /// Example: `BrewCoffee`
- Pascal,
- /// kebab-case format: words are separated by hyphens, all lowercase.
- ///
- /// Example: `brew-coffee`
- Kebab,
- /// dot.case format: words are separated by dots, all lowercase.
- ///
- /// Example: `brew.coffee`
- Dot,
- /// Title Case format: words are separated by spaces, each word capitalized.
- ///
- /// Example: `Brew Coffee`
- Title,
- /// lower case format: words are separated by spaces, all lowercase.
- ///
- /// Example: `brew coffee`
- Lower,
- /// UPPER CASE format: words are separated by spaces, all uppercase.
- ///
- /// Example: `BREW COFFEE`
- Upper,
-}
-
-impl ParserStyleNamingCase {
- /// Converts the input string `s` to the naming case represented by this variant.
- ///
- /// This method takes any type `S` that can be converted into a `String` and
- /// produced from a `String`, applies the corresponding case transformation,
- /// and returns the result.
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling_picker::parselib::ParserStyleNamingCase;
- ///
- /// let camel = ParserStyleNamingCase::Camel;
- /// assert_eq!(camel.convert("brew_coffee".to_string()), "brewCoffee");
- ///
- /// let kebab = ParserStyleNamingCase::Kebab;
- /// assert_eq!(kebab.convert("BrewCoffee".to_string()), "brew-coffee");
- /// ```
- pub fn convert<S>(&self, s: S) -> S
- where
- S: Into<String> + From<String>,
- {
- match self {
- ParserStyleNamingCase::Camel => just_fmt::camel_case!(s.into()).into(),
- ParserStyleNamingCase::Pascal => just_fmt::pascal_case!(s.into()).into(),
- ParserStyleNamingCase::Kebab => just_fmt::kebab_case!(s.into()).into(),
- ParserStyleNamingCase::Snake => just_fmt::snake_case!(s.into()).into(),
- ParserStyleNamingCase::Dot => just_fmt::dot_case!(s.into()).into(),
- ParserStyleNamingCase::Title => just_fmt::title_case!(s.into()).into(),
- ParserStyleNamingCase::Lower => just_fmt::lower_case!(s.into()).into(),
- ParserStyleNamingCase::Upper => just_fmt::upper_case!(s.into()).into(),
- }
- }
-}
-
-/// Unix-like style (e.g., `--verbose`, `-v`, `--name=value`)
-pub const UNIX_STYLE: ParserStyle = ParserStyle {
- end_of_options: "--",
- long_prefix: "--",
- short_prefix: "-",
- combine_prefix: "-",
- value_separator: '=',
- case_sensitive: true,
- allow_combine: true,
- naming_case: Kebab,
-};
-
-/// PowerShell style (e.g., `-Verbose`, `-Name:value`)
-pub const POWERSHELL_STYLE: ParserStyle = ParserStyle {
- end_of_options: "--",
- long_prefix: "-",
- short_prefix: "-",
- combine_prefix: "-",
- value_separator: ':',
- case_sensitive: false,
- allow_combine: false,
- naming_case: Pascal,
-};
-
-/// Windows-style command-line (e.g., `/Verbose`, `/Name:value`)
-pub const WINDOWS_STYLE: ParserStyle = ParserStyle {
- end_of_options: "--",
- long_prefix: "/",
- short_prefix: "/",
- combine_prefix: "/",
- value_separator: ':',
- case_sensitive: false,
- allow_combine: false,
- naming_case: Pascal,
-};
-
-static GLOBAL_STYLE: OnceLock<ParserStyle<'static>> = OnceLock::new();
-static GLOBAL_STYLE_SET: AtomicBool = AtomicBool::new(false);
-
-impl<'a> ParserStyle<'a> {
- /// Sets the global parser style.
- ///
- /// This function can only be called once. Subsequent calls will have no effect.
- /// The style is stored as a static reference; the provided style must be a static
- /// constant (e.g., `&'static ParserStyle`). Use the built-in constants like
- /// `UNIX_STYLE`, `POWERSHELL_STYLE`, or `WINDOWS_STYLE`.
- pub fn set_global_style(style: &'static ParserStyle<'static>) {
- if !GLOBAL_STYLE_SET.load(Ordering::Acquire) && GLOBAL_STYLE.set(*style).is_ok() {
- GLOBAL_STYLE_SET.store(true, Ordering::Release);
- }
- }
-
- /// Returns the global parser style, falling back to `UNIX_STYLE` if not set.
- pub fn global_style() -> &'static ParserStyle<'static> {
- GLOBAL_STYLE.get().unwrap_or(&UNIX_STYLE)
- }
-}
diff --git a/mingling_picker/src/parselib/utils.rs b/mingling_picker/src/parselib/utils.rs
deleted file mode 100644
index 47c5b55..0000000
--- a/mingling_picker/src/parselib/utils.rs
+++ /dev/null
@@ -1,276 +0,0 @@
-use crate::{
- PickerArgInfo,
- parselib::{MaskedArg, ParserStyle},
-};
-
-/// Builds a list of possible flag strings for the given argument info
-///
-/// This function generates formatted flag strings (e.g., `-h`, `--help`) from the short flag,
-/// long flag, and any aliases defined in the argument info. The long flag and alias names
-/// are converted according to the style's naming case convention before being formatted.
-#[inline(always)]
-pub fn build_possible_flags(style: &ParserStyle, arg_info: &PickerArgInfo) -> Vec<String> {
- let mut possible_flags = vec![];
-
- if let Some(short) = arg_info.short {
- possible_flags.push(style.flag_string(short));
- }
-
- if let Some(long) = arg_info.long {
- let converted = style.naming_case.convert(long.to_string());
- possible_flags.push(style.flag_string(&converted));
- }
-
- if let Some(aliases) = &arg_info.alias {
- for alias in aliases {
- let converted = style.naming_case.convert(alias.to_string());
- possible_flags.push(style.flag_string(&converted));
- }
- }
-
- possible_flags
-}
-
-/// Extract a single value from the raw strings tagged by [`SingleMatcher`](crate::parselib::SingleMatcher).
-///
-/// Returns `None` if no value is available (empty slice),
-/// the inline value after the style separator if present (eq mode),
-/// or the value directly (positional or flag-following).
-///
-/// This is the standard `pick` helper for all `Single`-type
-/// [`Pickable`](crate::Pickable) implementations.
-#[must_use]
-pub fn seek_single<'a>(raw_strs: &'a [&'a str]) -> Option<&'a str> {
- match raw_strs.len() {
- 0 => None,
- 1 => {
- let s = raw_strs[0];
- let sep = ParserStyle::global_style().value_separator;
- if let Some(pos) = s.rfind(sep) {
- Some(&s[pos + 1..])
- } else {
- Some(s)
- }
- }
- _ => Some(raw_strs[1]),
- }
-}
-
-/// Seeks the index of the end-of-options marker (`--`) in the argument list.
-///
-/// This function searches for the standard end-of-options separator (`--`)
-/// in the given argument list, respecting the parser's style settings
-/// (e.g., case sensitivity). The end-of-options marker indicates that all
-/// subsequent arguments should be treated as positional arguments, not flags.
-#[must_use]
-pub fn seek_end_of_options(args: &[MaskedArg], style: &ParserStyle) -> Option<usize> {
- args.iter()
- .find(|arg| {
- if style.case_sensitive {
- arg.raw == style.end_of_options
- } else {
- arg.raw.eq_ignore_ascii_case(style.end_of_options)
- }
- })
- .map(|arg| arg.raw_idx)
-}
-
-/// Seeks arguments in `args` that are exactly equal to the given `string`.
-///
-/// Returns the indices of matching arguments.
-#[must_use]
-#[inline(always)]
-pub fn seek_eq(args: &[MaskedArg], string: &str, case_sensitive: bool) -> Vec<usize> {
- args.iter()
- .filter(|arg| {
- if case_sensitive {
- arg.raw == string
- } else {
- arg.raw.eq_ignore_ascii_case(string)
- }
- })
- .map(|arg| arg.raw_idx)
- .collect()
-}
-
-/// Seeks arguments in `args` that contain the given `string` as a substring.
-///
-/// Returns the indices of matching arguments.
-#[must_use]
-#[inline(always)]
-pub fn seek_contains(args: &[MaskedArg], string: &str, case_sensitive: bool) -> Vec<usize> {
- args.iter()
- .filter(|arg| {
- if case_sensitive {
- arg.raw.contains(string)
- } else {
- arg.raw.to_lowercase().contains(&string.to_lowercase())
- }
- })
- .map(|arg| arg.raw_idx)
- .collect()
-}
-
-/// Seeks arguments in `args` that start with the given `string`.
-///
-/// Returns the indices of matching arguments.
-#[must_use]
-#[inline(always)]
-pub fn seek_start_with(args: &[MaskedArg], string: &str, case_sensitive: bool) -> Vec<usize> {
- args.iter()
- .filter(|arg| {
- if case_sensitive {
- arg.raw.starts_with(string)
- } else {
- arg.raw.to_lowercase().starts_with(&string.to_lowercase())
- }
- })
- .map(|arg| arg.raw_idx)
- .collect()
-}
-
-/// Seeks arguments in `args` that end with the given `string`.
-///
-/// Returns the indices of matching arguments.
-#[must_use]
-#[inline(always)]
-pub fn seek_end_with(args: &[MaskedArg], string: &str, case_sensitive: bool) -> Vec<usize> {
- args.iter()
- .filter(|arg| {
- if case_sensitive {
- arg.raw.ends_with(string)
- } else {
- arg.raw.to_lowercase().ends_with(&string.to_lowercase())
- }
- })
- .map(|arg| arg.raw_idx)
- .collect()
-}
-
-/// Seeks arguments in `args` that are exactly equal to any of the given `strings`.
-///
-/// Returns the indices of matching arguments.
-#[must_use]
-#[inline(always)]
-pub fn multi_seek_eq(args: &[MaskedArg], strings: &[&str], case_sensitive: bool) -> Vec<usize> {
- args.iter()
- .filter(|arg| {
- if case_sensitive {
- strings.contains(&arg.raw)
- } else {
- strings.iter().any(|s| arg.raw.eq_ignore_ascii_case(s))
- }
- })
- .map(|arg| arg.raw_idx)
- .collect()
-}
-
-/// Seeks arguments in `args` that contain any of the given `strings` as a substring.
-///
-/// Returns the indices of matching arguments.
-#[must_use]
-#[inline(always)]
-pub fn multi_seek_contains(
- args: &[MaskedArg],
- strings: &[&str],
- case_sensitive: bool,
-) -> Vec<usize> {
- args.iter()
- .filter(|arg| {
- if case_sensitive {
- strings.iter().any(|s| arg.raw.contains(s))
- } else {
- let lower_raw = arg.raw.to_lowercase();
- strings
- .iter()
- .any(|s| lower_raw.contains(&s.to_lowercase()))
- }
- })
- .map(|arg| arg.raw_idx)
- .collect()
-}
-
-/// Seeks arguments in `args` that start with any of the given `strings`.
-///
-/// Returns the indices of matching arguments.
-#[must_use]
-#[inline(always)]
-pub fn multi_seek_start_with(
- args: &[MaskedArg],
- strings: &[&str],
- case_sensitive: bool,
-) -> Vec<usize> {
- args.iter()
- .filter(|arg| {
- if case_sensitive {
- strings.iter().any(|s| arg.raw.starts_with(s))
- } else {
- let lower_raw = arg.raw.to_lowercase();
- strings
- .iter()
- .any(|s| lower_raw.starts_with(&s.to_lowercase()))
- }
- })
- .map(|arg| arg.raw_idx)
- .collect()
-}
-
-/// Seeks arguments in `args` that end with any of the given `strings`.
-///
-/// Returns the indices of matching arguments.
-#[must_use]
-#[inline(always)]
-pub fn multi_seek_end_with(
- args: &[MaskedArg],
- strings: &[&str],
- case_sensitive: bool,
-) -> Vec<usize> {
- args.iter()
- .filter(|arg| {
- if case_sensitive {
- strings.iter().any(|s| arg.raw.ends_with(s))
- } else {
- let lower_raw = arg.raw.to_lowercase();
- strings
- .iter()
- .any(|s| lower_raw.ends_with(&s.to_lowercase()))
- }
- })
- .map(|arg| arg.raw_idx)
- .collect()
-}
-
-/// Converts a `&Vec<String>` into a `Vec<&str>` by borrowing each string's slice.
-///
-/// This is useful for converting owned `String` vectors into borrowed `&str` slices
-/// for functions that take `&[&str]` or similar parameters.
-#[must_use]
-#[inline(always)]
-#[doc(hidden)]
-pub fn vec_string_to_vec_str(input: &[String]) -> Vec<&str> {
- input.iter().map(|s| s.as_str()).collect()
-}
-
-/// Converts a `&Vec<String>` into a `Vec<&str>` by borrowing each string's slice.
-///
-/// This is useful for converting owned `String` vectors into borrowed `&str` slices
-/// for functions that take `&[&str]` or similar parameters.
-#[macro_export]
-#[doc(hidden)]
-macro_rules! vec_string_slice {
- ($v:expr) => {
- $v.iter()
- .map(|s| s.as_str())
- .collect::<Vec<&str>>()
- .as_slice()
- };
-}
-
-/// Gets the first element from a vector of seek results, if any.
-///
-/// Returns `Some(index)` if the vector is non-empty, otherwise `None`.
-#[must_use]
-#[inline(always)]
-pub fn get_seeked_first(seeked: Vec<usize>) -> Option<usize> {
- seeked.into_iter().next()
-}
diff --git a/mingling_picker/src/pickable.rs b/mingling_picker/src/pickable.rs
deleted file mode 100644
index 758ae9a..0000000
--- a/mingling_picker/src/pickable.rs
+++ /dev/null
@@ -1,91 +0,0 @@
-use crate::{PickerArg, PickerArgAttr, PickerArgInfo, PickerArgResult, PickerArgs};
-
-mod single_pickable;
-pub use single_pickable::*;
-
-mod multi_pickable;
-pub use multi_pickable::*;
-
-/// `Pickable` trait defines how to parse a type instance from command-line arguments.
-///
-/// This trait is the core abstraction of the `Picker` argument parsing system, dividing the
-/// parsing process into two phases:
-///
-/// 1. **Tag phase ([`Pickable::tag`])**: Determines which argument positions the `Pickable` needs to handle.
-/// 2. **Pick phase ([`Pickable::pick`])**: Converts the raw strings at the tagged positions into the actual type.
-///
-/// Types implementing this trait must also implement [`Default`], so that a default value
-/// can be used as a fallback when parsing fails.
-///
-/// # Type Parameters
-///
-/// * `'a` - Lifetime parameter, used to associate references in [`PickerArg`].
-pub trait Pickable<'a>
-where
- Self: Sized,
-{
- /// Returns the parse-order attribute of this flag.
- ///
- /// This attribute is used to inform the parser about the parse order
- /// between different `Pickable` types.
- /// See [`PickerArgAttr`] for specific ordering definitions.
- ///
- /// # Parameters
- ///
- /// * `flag` - The current flag instance, which contains a reference to `Self`.
- ///
- /// # Returns
- ///
- /// Returns a [`PickerArgAttr`] describing the parse-order attribute of this flag.
- fn get_attr(flag: &'a PickerArg<'a, Self>) -> PickerArgAttr;
-
- /// Tag phase: Determines which argument positions the `Pickable` needs to handle.
- ///
- /// This function receives a [`TagPhaseContext`] containing argument context information.
- /// During this phase, the parser invokes each `Pickable` and collects the position indices
- /// they return, in order to determine which arguments to parse later.
- ///
- /// # Parameters
- ///
- /// * `ctx` - The tag phase context, containing argument information, all parameters of the
- /// current Picker, and an availability mask.
- ///
- /// # Returns
- ///
- /// Returns a `Vec<usize>` representing the indices of the arguments in the argument list
- /// that this `Pickable` needs to handle.
- fn tag(ctx: TagPhaseContext) -> Vec<usize>;
-
- /// Pick phase: Converts the raw string arguments tagged during the `tag` phase into
- /// the actual expected type.
- ///
- /// This function receives a slice of the raw strings that were tagged in the `tag` step
- /// and converts them into an instance of `Self`.
- ///
- /// # Parameters
- ///
- /// * `raw_strs` - A slice of strings containing the raw argument values to parse.
- ///
- /// # Returns
- ///
- /// Returns [`PickerArgResult<Self>`], i.e., the `Self` instance on success, or an appropriate
- /// error message on failure.
- fn pick(raw_strs: &[&str]) -> PickerArgResult<Self>;
-}
-
-/// Tag phase context, providing the necessary argument and state information for
-/// [`Pickable::tag`].
-pub struct TagPhaseContext<'a> {
- /// Argument information describing the structure and metadata of the argument
- /// to be parsed.
- pub arg_info: &'a PickerArgInfo<'a>,
-
- /// A read-only list of all arguments in the current [`Picker`](crate::Picker).
- pub args: &'a PickerArgs<'a>,
-
- /// Mask indicating which argument positions have already been claimed.
- ///
- /// For example, if the mask is `[0, 0, 1, 0]`, then the argument at index `2`
- /// has already been tagged by another `Pickable`.
- pub mask: &'a [u8],
-}
diff --git a/mingling_picker/src/pickable/multi_pickable.rs b/mingling_picker/src/pickable/multi_pickable.rs
deleted file mode 100644
index 84a8068..0000000
--- a/mingling_picker/src/pickable/multi_pickable.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use crate::{
- Pickable, PickerArg, PickerArgAttr, PickerArgResult, SinglePickable, TagPhaseContext,
- matcher_needed::Matcher,
- parselib::{MultiArgMatcher, ParserStyle},
-};
-
-/// Boundary check for multi-value positional parameters.
-pub trait BoundaryCheck {
- fn check_boundary(raw: &str) -> bool;
-}
-
-/// Trait for multi-value parameters.
-pub trait MultiPickableWithBoundary: Sized {
- type Checker: BoundaryCheck;
- fn pick_multi(raw: Vec<String>) -> PickerArgResult<Self>;
-}
-
-/// Marker: unit type that always accepts — no boundary.
-pub struct NoBoundary;
-
-impl BoundaryCheck for NoBoundary {
- #[inline(always)]
- fn check_boundary(_raw: &str) -> bool {
- false
- }
-}
-
-/// `Vec<T>` is greedy — it takes everything with `NoBoundary`.
-impl<T: SinglePickable> MultiPickableWithBoundary for Vec<T> {
- type Checker = NoBoundary;
-
- fn pick_multi(raw: Vec<String>) -> PickerArgResult<Self> {
- let mut result = Vec::with_capacity(raw.len());
- for s in &raw {
- match T::pick_single(Some(s)) {
- PickerArgResult::Parsed(v) => result.push(v),
- PickerArgResult::NotFound => return PickerArgResult::NotFound,
- PickerArgResult::Unparsed => {}
- }
- }
- PickerArgResult::Parsed(result)
- }
-}
-
-/// If the first raw string looks like a named flag (starts with the
-/// style's long or short prefix), strip it — it's the flag, not a value.
-fn strip_flag<'a>(raw_strs: &'a [&'a str]) -> &'a [&'a str] {
- if let Some(first) = raw_strs.first() {
- let style = ParserStyle::global_style();
- if first.starts_with(style.long_prefix) || first.starts_with(style.short_prefix) {
- return &raw_strs[1..];
- }
- }
- raw_strs
-}
-
-// Pickable impl for Vec<T>
-
-impl<'a, T> Pickable<'a> for Vec<T>
-where
- T: SinglePickable,
-{
- fn get_attr(flag: &'a PickerArg<'a, Self>) -> PickerArgAttr {
- PickerArgAttr::positional_or_multi(flag)
- }
-
- fn tag(ctx: TagPhaseContext) -> Vec<usize> {
- MultiArgMatcher::match_all(ctx.into())
- }
-
- fn pick(raw_strs: &[&str]) -> PickerArgResult<Self> {
- let strs = strip_flag(raw_strs);
- let owned: Vec<String> = strs.iter().map(|&s| s.to_string()).collect();
- <Vec<T> as MultiPickableWithBoundary>::pick_multi(owned)
- }
-}
diff --git a/mingling_picker/src/pickable/single_pickable.rs b/mingling_picker/src/pickable/single_pickable.rs
deleted file mode 100644
index 8a5b3e6..0000000
--- a/mingling_picker/src/pickable/single_pickable.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-use crate::{Pickable, PickerArg, PickerArgAttr, PickerArgResult, TagPhaseContext};
-
-/// `SinglePickable` trait defines how to parse a type from a single command-line argument.
-///
-/// This trait provides a simplified interface for types that consume exactly one argument value.
-/// It is automatically implemented by the blanket `impl` of [`Pickable`], so types implementing
-/// `SinglePickable` will work with the full `Pickable` argument parsing system.
-///
-/// Additionally, `Option<S>` where `S: SinglePickable` also implements [`Pickable`], allowing
-/// optional arguments to be parsed naturally.
-///
-/// # Type Parameters
-///
-/// * `Self` - The type to be parsed from a single argument string.
-pub trait SinglePickable
-where
- Self: Sized,
-{
- /// Parse a single optional string value into an instance of `Self`.
- ///
- /// # Parameters
- ///
- /// * `str` - An `Option<&str>` representing the raw argument value. If `None`,
- /// it indicates that no argument value was provided (e.g., for flag-like arguments).
- ///
- /// # Returns
- ///
- /// Returns [`PickerArgResult<Self>`], i.e., the parsed `Self` instance on success,
- /// or an appropriate error message on failure.
- fn pick_single(str: Option<&str>) -> PickerArgResult<Self>;
-}
-
-impl<'a, S> Pickable<'a> for S
-where
- S: SinglePickable,
-{
- fn get_attr(flag: &'a PickerArg<'a, Self>) -> PickerArgAttr {
- PickerArgAttr::positional_or_single(flag)
- }
-
- fn tag(ctx: TagPhaseContext) -> Vec<usize> {
- crate::parselib::SingleMatcher::tag(ctx)
- }
-
- fn pick(raw_strs: &[&str]) -> PickerArgResult<Self> {
- Self::pick_single(crate::parselib::seek_single(raw_strs))
- }
-}
-
-impl<'a, S> Pickable<'a> for Option<S>
-where
- S: SinglePickable,
-{
- fn get_attr(flag: &'a PickerArg<'a, Self>) -> PickerArgAttr {
- PickerArgAttr::positional_or_single(flag)
- }
-
- fn tag(ctx: TagPhaseContext) -> Vec<usize> {
- crate::parselib::SingleMatcher::tag(ctx)
- }
-
- fn pick(raw_strs: &[&str]) -> PickerArgResult<Self> {
- match S::pick(raw_strs) {
- PickerArgResult::Unparsed => PickerArgResult::Unparsed,
- PickerArgResult::Parsed(r) => PickerArgResult::Parsed(Some(r)),
- PickerArgResult::NotFound => PickerArgResult::Parsed(None),
- }
- }
-}
diff --git a/mingling_picker/src/picker.rs b/mingling_picker/src/picker.rs
deleted file mode 100644
index 7cf1525..0000000
--- a/mingling_picker/src/picker.rs
+++ /dev/null
@@ -1,432 +0,0 @@
-use std::{marker::PhantomData, ops::Index};
-
-mod parse;
-
-mod patterns;
-pub use patterns::*;
-
-mod result;
-pub use result::*;
-
-use crate::{Pickable, PickerArg, PickerArgResult};
-
-#[doc = include_str!("../README.md")]
-pub struct Picker<'a, Route = ()> {
- pub(crate) route_phantom: PhantomData<Route>,
-
- /// Internal arguments of Picker
- pub(crate) args: PickerArgs<'a>,
-}
-
-impl<'a> Picker<'a> {
- /// Creates a new `Picker` from the command-line arguments (excluding the program name).
- ///
- /// This is equivalent to calling `std::env::args().skip(1)`, which
- /// collects all arguments passed to the program except the first one
- /// (the executable path).
- pub fn from_args() -> Picker<'a, ()> {
- Self::from_args_skip(1)
- }
-
- /// Creates a new `Picker` from the command-line arguments, skipping the
- /// first `skip` entries.
- ///
- /// This method is useful when you want more control over which arguments
- /// are included. For example, pass `skip = 2` to skip both the program
- /// name and the first argument.
- pub fn from_args_skip(skip: usize) -> Picker<'a, ()> {
- let args = std::env::args().skip(skip).collect::<Vec<String>>();
- Picker {
- route_phantom: PhantomData,
- args: PickerArgs::Owned(args),
- }
- }
-
- /// Changes the route (phantom type parameter) of the `Picker`.
- ///
- /// This method allows converting a `Picker` from one route type to another,
- /// while preserving the same underlying arguments. The route type is typically
- /// used to distinguish different parsing contexts or to carry compile-time
- /// state information through the picking chain.
- pub fn with_route<NewRoute>(self) -> Picker<'a, NewRoute>
- where
- Self: Sized,
- {
- Picker {
- route_phantom: PhantomData,
- args: self.args,
- }
- }
-}
-
-/// Internal arguments of Picker
-///
-/// - `Slice` - borrowed slice of string slices
-/// - `Vec` - owned vector of borrowed string slices
-/// - `Owned` - owned vector of owned strings
-pub enum PickerArgs<'a> {
- /// Borrowed slice of string slices
- Slice(&'a [&'a str]),
- /// Owned vector of borrowed string slices
- Vec(Vec<&'a str>),
- /// Owned vector of owned strings
- Owned(Vec<String>),
-}
-
-impl<'a> Default for PickerArgs<'a> {
- fn default() -> Self {
- Self::Vec(vec![])
- }
-}
-
-impl<'a> PickerArgs<'a> {
- /// Returns the number of arguments.
- pub fn len(&self) -> usize {
- match self {
- PickerArgs::Slice(items) => items.len(),
- PickerArgs::Vec(items) => items.len(),
- PickerArgs::Owned(items) => items.len(),
- }
- }
-
- /// Returns `true` if there are no arguments.
- pub fn is_empty(&self) -> bool {
- self.len() == 0
- }
-
- /// Returns an iterator over the arguments, yielding `&str` values.
- pub fn iter(&'a self) -> PickerIter<'a> {
- match self {
- PickerArgs::Slice(items) => PickerIter::Slice(items.iter()),
- PickerArgs::Vec(items) => PickerIter::Vec(items.iter()),
- PickerArgs::Owned(items) => PickerIter::Owned(items.iter()),
- }
- }
-
- /// Returns a reference to the argument at `index`, if it exists.
- pub fn get(&self, index: usize) -> Option<&str> {
- match self {
- PickerArgs::Slice(items) => items.get(index).copied(),
- PickerArgs::Vec(items) => items.get(index).copied(),
- PickerArgs::Owned(items) => items.get(index).map(|s| s.as_str()),
- }
- }
-}
-
-impl<'a> Index<usize> for PickerArgs<'a> {
- type Output = str;
-
- fn index(&self, index: usize) -> &Self::Output {
- match self {
- PickerArgs::Slice(items) => items[index],
- PickerArgs::Vec(items) => items[index],
- PickerArgs::Owned(items) => &items[index],
- }
- }
-}
-
-impl<'a> IntoIterator for &'a PickerArgs<'a> {
- type Item = &'a str;
- type IntoIter = PickerIter<'a>;
-
- fn into_iter(self) -> Self::IntoIter {
- match self {
- PickerArgs::Slice(items) => PickerIter::Slice(items.iter()),
- PickerArgs::Vec(items) => PickerIter::Vec(items.iter()),
- PickerArgs::Owned(items) => PickerIter::Owned(items.iter()),
- }
- }
-}
-
-impl<'a, Route> From<&'a [&'a str]> for Picker<'a, Route> {
- fn from(value: &'a [&'a str]) -> Self {
- Picker {
- route_phantom: PhantomData,
- args: PickerArgs::Slice(value),
- }
- }
-}
-
-impl<'a, Route> From<Vec<&'a str>> for Picker<'a, Route> {
- fn from(value: Vec<&'a str>) -> Self {
- Picker {
- route_phantom: PhantomData,
- args: PickerArgs::Vec(value),
- }
- }
-}
-
-impl<'a, Route> From<Vec<String>> for Picker<'a, Route> {
- fn from(value: Vec<String>) -> Self {
- Picker {
- route_phantom: PhantomData,
- args: PickerArgs::Owned(value),
- }
- }
-}
-
-impl<'a, Route> Picker<'a, Route> {
- /// Returns a reference to the internal `PickerArgs`.
- pub fn args(&self) -> &PickerArgs<'a> {
- &self.args
- }
-
- /// Returns a mutable reference to the internal `PickerArgs`.
- pub fn args_mut(&mut self) -> &mut PickerArgs<'a> {
- &mut self.args
- }
-
- /// Consumes `self` and returns the internal `PickerArgs`.
- pub fn into_args(self) -> PickerArgs<'a> {
- self.args
- }
-
- /// Returns the number of arguments.
- pub fn len(&self) -> usize {
- self.args.len()
- }
-
- /// Returns `true` if there are no arguments.
- pub fn is_empty(&self) -> bool {
- self.args.is_empty()
- }
-
- /// Returns an iterator over the arguments, yielding `&str` values.
- pub fn iter(&'a self) -> PickerIter<'a> {
- self.args.iter()
- }
-}
-
-impl<'a, Route> Index<usize> for Picker<'a, Route> {
- type Output = str;
-
- fn index(&self, index: usize) -> &Self::Output {
- &self.args[index]
- }
-}
-
-impl<'a, Route> Index<usize> for &Picker<'a, Route> {
- type Output = str;
-
- fn index(&self, index: usize) -> &Self::Output {
- &self.args[index]
- }
-}
-
-impl<'a, Route> IntoIterator for &'a Picker<'a, Route> {
- type Item = &'a str;
- type IntoIter = PickerIter<'a>;
-
- fn into_iter(self) -> Self::IntoIter {
- self.args.iter()
- }
-}
-
-/// Iterator for `Picker` (and `PickerArgs`), yielding `&'a str` values.
-pub enum PickerIter<'a> {
- /// Iterates over a borrowed slice (`&[&str]`)
- Slice(std::slice::Iter<'a, &'a str>),
- /// Iterates over an owned vector of borrowed string slices (`Vec<&str>`)
- Vec(std::slice::Iter<'a, &'a str>),
- /// Iterates over an owned vector of owned strings (`Vec<String>`)
- Owned(std::slice::Iter<'a, String>),
-}
-
-impl<'a> Iterator for PickerIter<'a> {
- type Item = &'a str;
-
- fn next(&mut self) -> Option<Self::Item> {
- match self {
- PickerIter::Slice(iter) => iter.next().copied(),
- PickerIter::Vec(iter) => iter.next().copied(),
- PickerIter::Owned(iter) => iter.next().map(|s| s.as_str()),
- }
- }
-
- fn size_hint(&self) -> (usize, Option<usize>) {
- match self {
- PickerIter::Slice(iter) => iter.size_hint(),
- PickerIter::Vec(iter) => iter.size_hint(),
- PickerIter::Owned(iter) => iter.size_hint(),
- }
- }
-}
-
-impl<'a> ExactSizeIterator for PickerIter<'a> {}
-
-impl<'a, Route> Picker<'a, Route> {
- /// Creates a `PickerPattern1` from the given arg to start a picking chain.
- ///
- /// This method initiates a parameter picking chain with one arg.
- /// The result is initially `Unparsed`.
- pub fn pick<N>(self, arg: impl Into<&'a PickerArg<'a, N>>) -> PickerPattern1<'a, N, Route>
- where
- N: Pickable<'a>,
- {
- Self::build_pattern1(self.args, arg.into(), None::<Route>)
- }
-
- /// Creates a `PickerPattern1` from the given arg.
- /// If parsing fails, attempts the fallback arg.
- pub fn pick_or<N, F>(
- self,
- arg: impl Into<&'a PickerArg<'a, N>>,
- or_arg: F,
- ) -> PickerPattern1<'a, N, Route>
- where
- N: Pickable<'a>,
- F: FnMut() -> N + 'static,
- {
- self.pick(arg).or(or_arg)
- }
-
- /// Creates a `PickerPattern1` from the given arg.
- /// If parsing fails, uses the provided default value.
- pub fn pick_or_default<N>(
- self,
- arg: impl Into<&'a PickerArg<'a, N>>,
- ) -> PickerPattern1<'a, N, Route>
- where
- N: Pickable<'a> + Default,
- {
- self.pick(arg).or_default()
- }
-
- /// Creates a `PickerPattern1` from the given arg.
- /// If parsing fails, switches to the given error route.
- pub fn pick_or_route<N, F>(
- self,
- arg: impl Into<&'a PickerArg<'a, N>>,
- error_route: F,
- ) -> PickerPattern1<'a, N, Route>
- where
- N: Pickable<'a>,
- F: FnMut() -> Route + 'static,
- {
- self.pick(arg).or_route(error_route)
- }
-}
-
-/// Trait for converting types into a `Picker`
-///
-/// Implemented for:
-/// - `&[&str]` (borrowed slice)
-/// - `&[String]` (borrowed slice of owned strings)
-/// - `Vec<&str>` (owned vector of borrowed strings)
-/// - `Vec<String>` (owned vector of owned strings)
-pub trait IntoPicker<'a> {
- /// Converts the value into a `Picker`
- ///
- /// # Examples
- ///
- /// ```
- /// use mingling_picker::{IntoPicker, Picker};
- ///
- /// let args: Picker = (&["hello", "world"][..]).to_picker();
- /// assert_eq!(args.len(), 2);
- ///
- /// let args: Picker = vec!["foo", "bar"].to_picker();
- /// assert_eq!(args.len(), 2);
- ///
- /// let args: Picker = vec!["a".to_string(), "b".to_string()].to_picker();
- /// assert_eq!(args.len(), 2);
- /// ```
- fn to_picker(self) -> Picker<'a, ()>;
-
- /// Creates a `PickerPattern1` from the given arg for the `pick` method.
- ///
- /// This method converts the value into a `Picker` and starts a parameter
- /// picking chain with one arg. The result is initially `Unparsed`.
- fn pick<N>(self, arg: impl Into<&'a PickerArg<'a, N>>) -> PickerPattern1<'a, N, ()>
- where
- Self: Sized,
- N: Pickable<'a> + Default + Sized,
- {
- Picker::build_pattern1(self.to_picker().args, arg.into(), None::<()>)
- }
-
- /// Converts the value into a `Picker` with a specified route type.
- ///
- /// This method allows changing the route (phantom type parameter) of the picker.
- /// The route type is typically used to distinguish different parsing contexts or
- /// to carry compile-time state information through the picking chain.
- fn with_route<NewRoute>(self) -> Picker<'a, NewRoute>
- where
- Self: Sized,
- {
- Picker {
- route_phantom: PhantomData,
- args: self.to_picker().args,
- }
- }
-}
-
-impl<'a> IntoPicker<'a> for &'a [&'a str] {
- fn to_picker(self) -> Picker<'a, ()> {
- Picker {
- route_phantom: PhantomData,
- args: PickerArgs::Slice(self),
- }
- }
-}
-
-impl<'a> IntoPicker<'a> for &'a [String] {
- fn to_picker(self) -> Picker<'a, ()> {
- let vec: Vec<&str> = self.iter().map(|s| s.as_str()).collect();
- Picker {
- route_phantom: PhantomData,
- args: PickerArgs::Vec(vec),
- }
- }
-}
-
-impl<'a> IntoPicker<'a> for Vec<&'a str> {
- fn to_picker(self) -> Picker<'a, ()> {
- Picker {
- route_phantom: PhantomData,
- args: PickerArgs::Vec(self),
- }
- }
-}
-
-impl<'a> IntoPicker<'a> for &'a Vec<String> {
- fn to_picker(self) -> Picker<'a, ()> {
- let slice: Vec<&str> = self.iter().map(|s| s.as_str()).collect();
- Picker {
- route_phantom: PhantomData,
- args: PickerArgs::Vec(slice),
- }
- }
-}
-
-impl<'a> IntoPicker<'a> for Vec<String> {
- fn to_picker(self) -> Picker<'a, ()> {
- Picker {
- route_phantom: PhantomData,
- args: PickerArgs::Owned(self),
- }
- }
-}
-
-// Private helper: shared construction logic for `PickerPattern1`.
-// Both `Picker::pick` and `IntoPicker::pick` delegate to this.
-impl<'a, Route> Picker<'a, Route> {
- pub(crate) fn build_pattern1<N>(
- args: PickerArgs<'a>,
- arg: &'a PickerArg<'a, N>,
- error_route: Option<Route>,
- ) -> PickerPattern1<'a, N, Route>
- where
- N: Pickable<'a>,
- {
- PickerPattern1 {
- args,
- error_route,
- arg_1: arg,
- result_1: PickerArgResult::Unparsed,
- route_1: None,
- default_1: None,
- post_1: None,
- }
- }
-}
diff --git a/mingling_picker/src/picker/parse.rs b/mingling_picker/src/picker/parse.rs
deleted file mode 100644
index 698baac..0000000
--- a/mingling_picker/src/picker/parse.rs
+++ /dev/null
@@ -1,177 +0,0 @@
-// --------------------------------------------------------------------------------------------
-// I have to say, the code generated by this `internal_repeat!` macro is really UGLY.
-//
-// But I have to admit, this is a **trade-off**. To achieve the syntax of `pick().pick().pick()`
-// while ensuring type safety, this is the best approach I could think of.
-//
-// P.S. If there's a better way, please let me know. Thanks!
-// --------------------------------------------------------------------------------------------
-//
-// Then, I must disable `clippy::type_complexity` — this guy is way too noisy.
-#![allow(clippy::type_complexity)]
-
-use crate::{Pickable, PickerArgAttr, PickerArgInfo, PickerArgResult, PickerArgs, TagPhaseContext};
-use mingling_picker_macros::internal_repeat;
-
-internal_repeat!(1..=32 => {
- use crate::PickerPattern$;
- use crate::PickerResult$;
-});
-
-internal_repeat!(1..=32 => {
- impl<'a, (T$,+), Route> PickerPattern$<'a, (T$,+), Route>
- where (T$: Pickable<'a>,+) {
- /// Unwraps the result, panicking if a route was selected.
- ///
- /// # Panics
- ///
- /// Panics if a route was selected.
- pub fn unwrap(self) -> ((T$,+)) {
- let p = self.parse();
- ((p.v$.unwrap(),+))
- }
-
- /// Returns the individual option values without checking the route.
- pub fn unpack(self) -> ((Option<T$>,+)) {
- let p = self.parse();
- ((p.v$,+))
- }
-
- /// Converts to a `Result`, returning `Err(route)` if a route was selected,
- /// or `Ok(values)` otherwise.
- pub fn to_result(self) -> Result<((T$,+)), Route> {
- let p = self.parse();
- if let Some(r) = p.route {
- return Err(r);
- }
- Ok(p.unwrap())
- }
-
- /// Converts to an `Option`, returning `None` if a route was selected,
- /// or `Some(values)` otherwise.
- pub fn to_option(self) -> Option<((T$,+))> {
- let p = self.parse();
- if p.route.is_some() {
- return None;
- }
- Some(p.unwrap())
- }
- }
-});
-
-internal_repeat!(1..=32 => {
- impl<'a, (T$,+), Route> PickerPattern$<'a, (T$,+), Route>
- where (T$: Pickable<'a>,+)
- {
- pub fn parse(mut self) -> PickerResult$<(T$,+), Route> {
- // ArgInfos
- let arg_infos: [PickerArgInfo; $] = [
- (
- PickerArgInfo::from(self.arg_$),
- +)
- ];
-
- let mut bundle: [
- (
- // Arg Attr
- PickerArgAttr,
-
- // Tag Func
- Box<dyn FnOnce(&PickerArgs<'a>, &[u8]) -> Vec<usize>>,
-
- // Pick Func
- Box<dyn FnOnce(&[&str], &mut Option<Route>)>,
-
- // Index
- usize
- )
- ; $] = [
- (
- (
- // Arg Attr
- T$::get_attr(self.arg_$),
-
- // Tag Func
- Box::new(|args, mask| {
- let ctx = TagPhaseContext {
- arg_info: &arg_infos[$-],
- args,
- mask
- };
- T$::tag(ctx)
- }),
-
- // Pick Func
- Box::new(|args, error_route| {
- self.result_$ = match T$::pick(args) {
- PickerArgResult::Parsed(mut value) => {
- // Postprocess
- if let Some(post) = self.post_$ {
- value = post(value);
- }
- PickerArgResult::Parsed(value)
- },
- other => {
- if let Some(get_default) = self.default_$ {
- let mut value = get_default();
-
- // Postprocess
- if let Some(post) = self.post_$ {
- value = post(value);
- }
-
- PickerArgResult::Parsed(value)
- } else {
- if error_route.is_none() {
- if let Some(get_route) = self.route_$ {
- *error_route = Some(get_route().into());
- }
- }
- other
- }
- },
-
- }
- }),
-
- // Index
- $
- ),
- +)
- ];
-
- // Sort by Bundle Ord (descending)
- bundle.sort_by(|a, b| b.0.cmp(&a.0));
-
- // Mask — size = number of args (not args), so use args length
- let mut mask: Vec<u8> = vec![0u8; self.args.len()];
-
- // Parsing
- for (_, tag_func, pick_func, _idx) in bundle {
-
- // Tag phase
- let tagged = tag_func(&self.args, mask.as_slice());
- let mut args_to_pick: Vec<&str> = vec![];
-
- for i in tagged {
- mask[i] = 1;
-
- // Update args to pick
- args_to_pick.push(self.args.get(i).unwrap_or_default());
- }
-
- // Pick phase
- pick_func(args_to_pick.as_slice(), &mut self.error_route);
- }
-
- // Combine Result
- let result: PickerResult$<(T$,+), Route> = PickerResult$ {
- route: self.error_route,
- (
- v$: self.result_$.to_option(),
- +)
- };
- result
- }
- }
-});
diff --git a/mingling_picker/src/picker/patterns.rs b/mingling_picker/src/picker/patterns.rs
deleted file mode 100644
index 78ae2b0..0000000
--- a/mingling_picker/src/picker/patterns.rs
+++ /dev/null
@@ -1,205 +0,0 @@
-use mingling_picker_macros::internal_repeat;
-
-use crate::{Pickable, PickerArg, PickerArgResult, PickerArgs};
-
-internal_repeat!(1..=32 => {
- #[doc(hidden)]
- pub struct PickerPattern$<'a, (T$,+), Route>
- where (T$: Pickable<'a>,+)
- {
- pub args: PickerArgs<'a>,
- pub error_route: Option<Route>,
- (
- pub(crate) arg_$: &'a PickerArg<'a, T$>,
- pub(crate) result_$: PickerArgResult<T$>,
- pub(crate) default_$: Option<Box<dyn FnOnce() -> T$>>,
- pub(crate) route_$: Option<Box<dyn FnOnce() -> Route>>,
- pub(crate) post_$: Option<Box<dyn FnOnce(T$) -> T$>>,
- +)
- }
-});
-
-internal_repeat!(1..=32 => {
- impl<'a, (T$,+), Route> PickerPattern$<'a, (T$,+), Route>
- where (T$: Pickable<'a>,+)
- {
- /// Sets a default value provider for this arg.
- ///
- /// If the arg is not provided by the user at runtime, the given closure will be
- /// called to produce a default value. The closure is expected to return `T$`.
- ///
- /// # Example
- ///
- #[allow(clippy::type_complexity)]
- pub fn or<F>(mut self, func: F) -> Self
- where
- F: FnMut() -> T$,
- F: 'static,
- {
- self.default_$ = Some(Box::new(func));
- self
- }
-
- /// Uses the default value for this arg's type if the arg is not provided.
- ///
- /// If the arg is not provided by the user at runtime, the default value for `T$`
- /// (as defined by the `Default` trait) will be used.
- ///
- /// # Example
- ///
- #[allow(clippy::type_complexity)]
- pub fn or_default(mut self) -> Self
- where
- T$: Default,
- {
- self.default_$ = Some(Box::new(|| T$::default()));
- self
- }
-
- /// Sets a route for when the arg is not provided.
- ///
- /// If the arg is not provided by the user at runtime, the given closure will be
- /// called to produce a route value that will be returned early.
- ///
- /// # Example
- ///
- pub fn or_route<F>(mut self, func: F) -> Self
- where
- F: FnMut() -> Route,
- F: 'static,
- {
- self.route_$ = Some(Box::new(func));
- self
- }
-
-
- /// Resets the route for this picker pattern, allowing a different route type.
- ///
- /// This method converts the current `PickerPattern` into a new one with a different
- /// route type `NewRoute`. All existing arg configurations, defaults, and post-
- /// processing functions are preserved, but the `error_route` and individual
- /// `route_$` fields are cleared (set to `None`).
- ///
- /// This is useful when you want to change the error/redirect route type mid-chain,
- /// for example when composing patterns from different contexts that use different
- /// route enums.
- #[allow(clippy::type_complexity)]
- pub fn with_route<NewRoute>(self) -> PickerPattern$<'a, (T$,+), NewRoute>
- {
- PickerPattern$ {
- args: self.args,
- error_route: None,
- (
- arg_$: self.arg_$,
- result_$: self.result_$,
- default_$: self.default_$,
- route_$: None,
- post_$: self.post_$,
- +)
- }
- }
-
- /// Attaches a post-processing function to this arg.
- ///
- /// After the arg's value is parsed (or defaulted), the given closure will be
- /// invoked with the parsed value and its return value will be used as the final
- /// result. This allows transforming or validating the parsed value.
- ///
- /// # Example
- ///
- #[allow(clippy::type_complexity)]
- pub fn post<F>(mut self, func: F) -> Self
- where
- F: FnMut(T$) -> T$,
- F: 'static,
- {
- self.post_$ = Some(Box::new(func));
- self
- }
- }
-});
-
-internal_repeat!(1..32 => {
- impl<'a, (T$,+), Route> PickerPattern$<'a, (T$,+), Route>
- where (T$: Pickable<'a>,+)
- {
- #[allow(clippy::type_complexity)]
- /// Adds a new arg to the picking chain, returning a new `PickerPattern` with one more type parameter.
- ///
- /// This method extends the current picking pattern by appending an additional arg.
- /// The previous args and their results are preserved as part of the new pattern.
- /// The new arg's result is initially `Unparsed`.
- pub fn pick<N>(self, arg: impl Into<&'a PickerArg<'a, N>>) -> PickerPattern$+<'a, (T$,+), N, Route>
- where
- N: Pickable<'a>,
- {
- PickerPattern$+ {
- // Args
- args: self.args,
- error_route: self.error_route,
-
- // Current
- arg_$+: arg.into(),
- result_$+: PickerArgResult::Unparsed,
- default_$+: None,
- route_$+: None,
- post_$+: None,
-
- // Prev
- (
- arg_$: self.arg_$,
- result_$: self.result_$,
- default_$: self.default_$,
- route_$: self.route_$,
- post_$: self.post_$,
- +)
- }
- }
-
- /// Picks a new arg with a default value provider in a single call.
- ///
- /// This is a shorthand for calling `.pick(arg).or(func)`.
- ///
- /// # Example
- ///
- #[allow(clippy::type_complexity)]
- pub fn pick_or<N, F>(self, arg: impl Into<&'a PickerArg<'a, N>>, func: F) -> PickerPattern$+<'a, (T$,+), N, Route>
- where
- N: Pickable<'a>,
- F: FnMut() -> N + 'static,
- F: 'static,
- {
- self.pick(arg).or(func)
- }
-
- /// Picks a new arg with a default value in a single call.
- ///
- /// This is a shorthand for calling `.pick(arg).or_default()`.
- ///
- /// # Example
- ///
- #[allow(clippy::type_complexity)]
- pub fn pick_or_default<N>(self, arg: impl Into<&'a PickerArg<'a, N>>) -> PickerPattern$+<'a, (T$,+), N, Route>
- where
- N: Pickable<'a> + Default,
- {
- self.pick(arg).or_default()
- }
-
- /// Picks a new arg with a route in a single call.
- ///
- /// This is a shorthand for calling `.pick(arg).or_route(func)`.
- ///
- /// # Example
- ///
- #[allow(clippy::type_complexity)]
- pub fn pick_or_route<N, F>(self, arg: impl Into<&'a PickerArg<'a, N>>, func: F) -> PickerPattern$+<'a, (T$,+), N, Route>
- where
- N: Pickable<'a>,
- F: FnMut() -> Route + 'static,
- F: 'static,
- {
- self.pick(arg).or_route(func)
- }
- }
-});
diff --git a/mingling_picker/src/picker/result.rs b/mingling_picker/src/picker/result.rs
deleted file mode 100644
index 9cd78ae..0000000
--- a/mingling_picker/src/picker/result.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-#![allow(clippy::type_complexity)] // Aha, Type Gymnastics!
-
-use mingling_picker_macros::internal_repeat;
-
-internal_repeat!(1..=32 => {
- #[doc(hidden)]
- pub struct PickerResult$<(T$,+), Route> {
- /// The route selected by the picker, if any.
- /// If this is `Some`, the picker chose to follow a route instead of selecting values,
- /// and all value fields (`v1`, `v2`, ...) will be `None`.
- ///
- /// Note: "route" here refers to an alternative path/choice, not a network route.
- pub route: Option<Route>,
-
- (
- #[doc = concat!("The optional value for the ", $, "th type parameter.")]
- pub v$: Option<T$>,
- +)
- }
-});
-
-internal_repeat!(1..=32 => {
- impl<(T$,+), Route> PickerResult$<(T$,+), Route> {
- /// Unwraps the result, panicking if a route was selected.
- ///
- /// # Panics
- ///
- /// Panics if `self.route` is `Some(...)`.
- pub fn unwrap(self) -> ((T$,+)) {
- ((self.v$.unwrap(),+))
- }
-
- /// Returns the individual option values without checking the route.
- pub fn unpack(self) -> ((Option<T$>,+)) {
- ((self.v$,+))
- }
-
- /// Converts to a `Result`, returning `Err(route)` if a route was selected,
- /// or `Ok(values)` otherwise.
- pub fn to_result(self) -> Result<((T$,+)), Route> {
- if let Some(r) = self.route {
- return Err(r);
- }
- Ok(self.unwrap())
- }
-
- /// Converts to an `Option`, returning `None` if a route was selected,
- /// or `Some(values)` otherwise.
- pub fn to_option(self) -> Option<((T$,+))> {
- if let Some(_) = self.route {
- return None;
- }
- Some(self.unwrap())
- }
- }
-});
diff --git a/mingling_picker/src/value.rs b/mingling_picker/src/value.rs
deleted file mode 100644
index 995aa00..0000000
--- a/mingling_picker/src/value.rs
+++ /dev/null
@@ -1,5 +0,0 @@
-mod flag;
-pub use flag::*;
-
-mod vec_until;
-pub use vec_until::*;
diff --git a/mingling_picker/src/value/flag.rs b/mingling_picker/src/value/flag.rs
deleted file mode 100644
index ee0d6ee..0000000
--- a/mingling_picker/src/value/flag.rs
+++ /dev/null
@@ -1,145 +0,0 @@
-use std::{
- fmt::{Debug, Display},
- ops::{Deref, Not},
-};
-
-/// Parsed result of a boolean-style command-line flag.
-///
-/// `Flag` is a **value type** that can be declared in [`PickerArg`].
-/// When the user passes `--verbose` on the command line, the parsed result is `Flag::Active`;
-/// when the flag is absent, the result is `Flag::Inactive`.
-///
-/// # Why not just `bool`?
-///
-/// Unlike a raw `bool`, `Flag` carries **explicit semantics** about whether
-/// the flag was actually provided by the user (`Active`) or simply omitted
-/// (`Inactive`). This distinction matters when you want to distinguish
-/// "the user intentionally omitted the flag" from "the flag was processed but
-/// resolved to false" — the `Pickable` implementation for `Flag` always
-/// returns `Parsed(Flag::Inactive)` when no matching argument is found,
-/// rather than `NotFound`, making it always succeed with a meaningful default.
-///
-/// # Conversions
-///
-/// `Flag` interoperates seamlessly with `bool`: `Flag::Active` is `true`,
-/// `Flag::Inactive` is `false`. The [`Deref`] impl allows using a `Flag`
-/// directly in boolean contexts:
-///
-/// ```
-/// # use mingling_picker::value::Flag;
-/// let flag = Flag::Active;
-/// if *flag { /* runs */ }
-/// ```
-///
-/// [`PickerArg`]: crate::PickerArg
-#[derive(Default, Clone, Copy, PartialEq, Eq)]
-pub enum Flag {
- /// The flag was **not** present on the command line.
- ///
- /// This is the default state, equivalent to `false`.
- #[default]
- Inactive,
-
- /// The flag **was** present on the command line.
- ///
- /// Equivalent to `true`.
- Active,
-}
-
-impl Debug for Flag {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Self::Inactive => write!(f, "inactive"),
- Self::Active => write!(f, "active"),
- }
- }
-}
-
-impl Display for Flag {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- match self {
- Self::Inactive => write!(f, "inactive"),
- Self::Active => write!(f, "active"),
- }
- }
-}
-
-impl Flag {
- /// Converts this `Flag` into a `bool`.
- ///
- /// Returns `true` if the flag is [`Active`], `false` if [`Inactive`].
- ///
- /// # Examples
- ///
- /// ```
- /// # use mingling_picker::value::Flag;
- /// assert!(Flag::Active.bool());
- /// assert!(!Flag::Inactive.bool());
- /// ```
- ///
- /// [`Active`]: Flag::Active
- /// [`Inactive`]: Flag::Inactive
- #[must_use]
- #[inline(always)]
- pub fn bool(&self) -> bool {
- *self == Flag::Active
- }
-}
-
-impl PartialEq<bool> for Flag {
- fn eq(&self, other: &bool) -> bool {
- self.bool() == *other
- }
-}
-
-/// Compares `bool` with `Flag` using `==`.
-impl PartialEq<Flag> for bool {
- fn eq(&self, other: &Flag) -> bool {
- *self == other.bool()
- }
-}
-
-impl From<bool> for Flag {
- fn from(value: bool) -> Self {
- if value { Flag::Active } else { Flag::Inactive }
- }
-}
-
-impl From<Flag> for bool {
- fn from(val: Flag) -> Self {
- val == Flag::Active
- }
-}
-
-/// Allows `Flag` to be used in boolean contexts via `*flag`.
-///
-/// # Examples
-///
-/// ```
-/// # use mingling_picker::value::Flag;
-/// let flag = Flag::Active;
-/// if *flag {
-/// println!("flag is set");
-/// }
-/// ```
-impl Deref for Flag {
- type Target = bool;
-
- fn deref(&self) -> &bool {
- match self {
- Flag::Active => &true,
- Flag::Inactive => &false,
- }
- }
-}
-
-impl Not for Flag {
- type Output = Flag;
-
- fn not(self) -> Flag {
- match self {
- Flag::Active => Flag::Inactive,
- Flag::Inactive => Flag::Active,
- }
- }
-}
diff --git a/mingling_picker/src/value/vec_until.rs b/mingling_picker/src/value/vec_until.rs
deleted file mode 100644
index 1b79641..0000000
--- a/mingling_picker/src/value/vec_until.rs
+++ /dev/null
@@ -1,134 +0,0 @@
-use std::marker::PhantomData;
-use std::ops::{Deref, DerefMut};
-
-use crate::{
- BoundaryCheck, MultiPickableWithBoundary, Pickable, PickerArg, PickerArgAttr, PickerArgResult,
- SinglePickable, TagPhaseContext,
- matcher_needed::Matcher,
- parselib::{MultiArgMatcher, ParserStyle},
-};
-
-/// A `Vec`-like container that stops collecting when [`BoundaryCheck`]
-/// returns `true`.
-///
-/// This type exists to signal "I know what I'm doing with boundaries"
-/// at the type level (as opposed to `Vec<T>` which greedily takes
-/// everything).
-#[derive(Debug, Clone, PartialEq, Eq, Default)]
-pub struct VecUntil<T> {
- pub(crate) inner: Vec<T>,
- _marker: PhantomData<T>,
-}
-
-impl<T> VecUntil<T> {
- pub fn into_inner(self) -> Vec<T> {
- self.inner
- }
-}
-
-impl<T> From<Vec<T>> for VecUntil<T> {
- fn from(v: Vec<T>) -> Self {
- VecUntil {
- inner: v,
- _marker: PhantomData,
- }
- }
-}
-
-impl<T> From<VecUntil<T>> for Vec<T> {
- fn from(v: VecUntil<T>) -> Self {
- v.inner
- }
-}
-
-impl<T> Deref for VecUntil<T> {
- type Target = Vec<T>;
- fn deref(&self) -> &Vec<T> {
- &self.inner
- }
-}
-
-impl<T> DerefMut for VecUntil<T> {
- fn deref_mut(&mut self) -> &mut Vec<T> {
- &mut self.inner
- }
-}
-
-// MultiPickableWithBoundary impl
-
-impl<T> MultiPickableWithBoundary for VecUntil<T>
-where
- T: SinglePickable + BoundaryCheck,
-{
- type Checker = T;
-
- fn pick_multi(raw: Vec<String>) -> PickerArgResult<Self> {
- let mut inner = Vec::with_capacity(raw.len());
- for s in &raw {
- match T::pick_single(Some(s)) {
- PickerArgResult::Parsed(v) => inner.push(v),
- PickerArgResult::NotFound => return PickerArgResult::NotFound,
- PickerArgResult::Unparsed => {}
- }
- }
- PickerArgResult::Parsed(VecUntil {
- inner,
- _marker: PhantomData,
- })
- }
-}
-
-// Pickable impl
-
-impl<'a, T> Pickable<'a> for VecUntil<T>
-where
- T: SinglePickable + BoundaryCheck,
-{
- fn get_attr(flag: &'a PickerArg<'a, Self>) -> PickerArgAttr {
- PickerArgAttr::positional_or_multi(flag)
- }
-
- fn tag(ctx: TagPhaseContext) -> Vec<usize> {
- let args = ctx.args;
- let is_positional = ctx.arg_info.positional;
- let positions = MultiArgMatcher::match_all(ctx.into());
- if positions.is_empty() {
- return positions;
- }
-
- let start = if is_positional { 0 } else { 1 };
- if start >= positions.len() {
- return positions;
- }
-
- let mut cut = start;
- for &idx in &positions[start..] {
- if let Some(raw) = args.get(idx)
- && T::check_boundary(raw)
- {
- break;
- }
- cut += 1;
- }
-
- positions[..cut].to_vec()
- }
-
- fn pick(raw_strs: &[&str]) -> PickerArgResult<Self> {
- let strs = strip_flag(raw_strs);
- let owned: Vec<String> = strs.iter().map(|&s| s.to_string()).collect();
- <VecUntil<T> as MultiPickableWithBoundary>::pick_multi(owned)
- }
-}
-
-/// If the first raw string looks like a named flag (starts with the
-/// style's long or short prefix), strip it — it's the flag, not a value.
-fn strip_flag<'a>(raw_strs: &'a [&'a str]) -> &'a [&'a str] {
- if let Some(first) = raw_strs.first() {
- let style = ParserStyle::global_style();
- if first.starts_with(style.long_prefix) || first.starts_with(style.short_prefix) {
- return &raw_strs[1..];
- }
- }
- raw_strs
-}
diff --git a/mingling_picker/test/Cargo.lock b/mingling_picker/test/Cargo.lock
deleted file mode 100644
index 0fef84b..0000000
--- a/mingling_picker/test/Cargo.lock
+++ /dev/null
@@ -1,68 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[package]]
-name = "just_fmt"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6170dccbc3ea15dfb7f2da964097f814aba1dd8f746d4ffc56f33245c38e6d96"
-
-[[package]]
-name = "mingling_picker"
-version = "0.3.0"
-dependencies = [
- "just_fmt",
- "mingling_picker_macros",
-]
-
-[[package]]
-name = "mingling_picker_macros"
-version = "0.3.0"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.106"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.46"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "syn"
-version = "2.0.119"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "test-mingling-picker"
-version = "0.1.0"
-dependencies = [
- "mingling_picker",
-]
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
diff --git a/mingling_picker/test/Cargo.toml b/mingling_picker/test/Cargo.toml
deleted file mode 100644
index 127546c..0000000
--- a/mingling_picker/test/Cargo.toml
+++ /dev/null
@@ -1,9 +0,0 @@
-[package]
-name = "test-mingling-picker"
-version = "0.1.0"
-edition = "2024"
-
-[workspace]
-
-[dependencies]
-mingling_picker = { path = "../" }
diff --git a/mingling_picker/test/src/lib.rs b/mingling_picker/test/src/lib.rs
deleted file mode 100644
index eac6cad..0000000
--- a/mingling_picker/test/src/lib.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Using `assert_eq!(x, true)` is clearer than `assert!(x)` for expressing expected values
-//
-// BECAUSE `assert!` only checks if the boolean value is true,
-// while `assert_eq!` explicitly shows the expected value
-#![allow(clippy::bool_assert_comparison)]
-
-#[cfg(test)]
-mod test;
-
-use mingling_picker::parselib::MaskedArg;
-
-/// Create a single `MaskedArg` from a raw string and its original index.
-pub fn make_masked(raw: &str, idx: usize) -> MaskedArg<'_> {
- MaskedArg { raw, raw_idx: idx }
-}
-
-/// Create a `Vec<MaskedArg>` from an array of `(raw, raw_idx)` pairs.
-pub fn make_args<'a>(pairs: &'a [(&'a str, usize)]) -> Vec<MaskedArg<'a>> {
- pairs
- .iter()
- .map(|&(raw, idx)| MaskedArg { raw, raw_idx: idx })
- .collect()
-}
diff --git a/mingling_picker/test/src/test.rs b/mingling_picker/test/src/test.rs
deleted file mode 100644
index 9c53514..0000000
--- a/mingling_picker/test/src/test.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-mod arg_matcher_test;
-mod basic_test;
-mod multi_arg_test;
-mod multi_value_test;
-mod pos_matcher_test;
-mod priority_test;
-mod route_test;
-mod style_test;
-mod value_flag_test;
-mod value_string_test;
diff --git a/mingling_picker/test/src/test/arg_matcher_test.rs b/mingling_picker/test/src/test/arg_matcher_test.rs
deleted file mode 100644
index 7b37bc8..0000000
--- a/mingling_picker/test/src/test/arg_matcher_test.rs
+++ /dev/null
@@ -1,289 +0,0 @@
-use mingling_picker::PickerArgInfo;
-use mingling_picker::parselib::{ArgMatcher, Matcher, POWERSHELL_STYLE, UNIX_STYLE};
-
-use crate::make_args;
-
-// on_match_one — Named
-
-#[test]
-fn test_match_one_named_basic() {
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = make_args(&[("--name", 0), ("Alice", 1)]);
- let result = ArgMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, Some(0));
-}
-
-#[test]
-fn test_match_one_named_eq_mode() {
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = make_args(&[("--name=Alice", 0)]);
- let result = ArgMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, Some(0));
-}
-
-#[test]
-fn test_match_one_named_no_match() {
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = make_args(&[("--other", 0), ("Alice", 1)]);
- let result = ArgMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, None);
-}
-
-#[test]
-fn test_match_one_named_short_flag() {
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- info.set_short('n');
- let args = make_args(&[("-n", 0), ("Alice", 1)]);
- let result = ArgMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, Some(0));
-}
-
-#[test]
-fn test_match_one_named_after_end_of_options() {
- // Flags after `--` should not be matched.
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = make_args(&[("--", 0), ("--name", 1), ("Alice", 2)]);
- let result = ArgMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, None);
-}
-
-// on_match_one — Positional
-
-#[test]
-fn test_match_one_positional_basic() {
- let mut info = PickerArgInfo::new();
- info.set_positional(true);
- let args = make_args(&[("file.txt", 0)]);
- let result = ArgMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, Some(0));
-}
-
-#[test]
-fn test_match_one_positional_takes_first() {
- let mut info = PickerArgInfo::new();
- info.set_positional(true);
- let args = make_args(&[("a.txt", 0), ("b.txt", 1)]);
- let result = ArgMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, Some(0));
-}
-
-// on_match_all — Named, single occurrence
-
-#[test]
-fn test_match_all_named_flag_plus_value() {
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = make_args(&[("--name", 0), ("Alice", 1)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1]);
-}
-
-#[test]
-fn test_match_all_named_eq_mode() {
- // --name=Alice: value is inline, only tag the flag position.
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = make_args(&[("--name=Alice", 0)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0]);
-}
-
-#[test]
-fn test_match_all_named_no_value() {
- // Flag at end with no following arg: only tag the flag.
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = make_args(&[("--name", 0)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0]);
-}
-
-#[test]
-fn test_match_all_named_value_looks_like_flag() {
- // The next arg looks like a flag — still tag it.
- // Validation is the Pickable's responsibility.
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = make_args(&[("--name", 0), ("--other", 1)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1]);
-}
-
-// on_match_all — Named, multiple occurrences (Single per flag)
-
-#[test]
-fn test_match_all_named_two_occurrences() {
- // --name Alice --name Bob → each occurrence gets one value.
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = make_args(&[("--name", 0), ("Alice", 1), ("--name", 2), ("Bob", 3)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1, 2, 3]);
-}
-
-#[test]
-fn test_match_all_named_skips_non_matching_args() {
- // --val a b --val d → only pairs (0,1) and (3,4); idx 2 ("b") left free.
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[("--val", 0), ("a", 1), ("b", 2), ("--val", 3), ("d", 4)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1, 3, 4]);
-}
-
-// on_match_all — Named, short flag
-
-#[test]
-fn test_match_all_named_short_flag() {
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- info.set_short('n');
- let args = make_args(&[("-n", 0), ("Alice", 1)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1]);
-}
-
-// on_match_all — Named, eq + non-eq mixed
-
-#[test]
-fn test_match_all_named_mixed_eq_and_regular() {
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = make_args(&[("--name=Alice", 0), ("--name", 1), ("Bob", 2)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1, 2]);
-}
-
-// on_match_all — Named, case insensitive (PowerShell)
-
-#[test]
-fn test_match_all_named_powershell_case_insensitive() {
- let mut info = PickerArgInfo::new();
- info.set_long("Name");
- let args = make_args(&[("-name", 0), ("Alice", 1)]);
- let result = ArgMatcher::on_match_all(&args, &POWERSHELL_STYLE, &info);
- assert_eq!(result, vec![0, 1]);
-}
-
-// on_match_all — Positional
-
-#[test]
-fn test_match_all_positional_single() {
- let mut info = PickerArgInfo::new();
- info.set_positional(true);
- let args = make_args(&[("file.txt", 0)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0]);
-}
-
-#[test]
-fn test_match_all_positional_multiple() {
- let mut info = PickerArgInfo::new();
- info.set_positional(true);
- let args = make_args(&[("a.txt", 0), ("b.txt", 1)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1]);
-}
-
-// End-of-options marker (`--`)
-
-#[test]
-fn test_match_all_named_stops_at_end_of_options() {
- // --name before `--` should match, --name after should not.
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = make_args(&[
- ("--name", 0),
- ("Alice", 1),
- ("--", 2),
- ("--name", 3),
- ("Bob", 4),
- ]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1]);
-}
-
-#[test]
-fn test_match_all_positional_stops_at_end_of_options() {
- let mut info = PickerArgInfo::new();
- info.set_positional(true);
- let args = make_args(&[("before", 0), ("--", 1), ("after", 2)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0]);
-}
-
-// Empty args
-
-#[test]
-fn test_match_one_empty() {
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = vec![];
- let result = ArgMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, None);
-}
-
-#[test]
-fn test_match_all_empty() {
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = vec![];
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert!(result.is_empty());
-}
-
-// Verify that -- itself is never matched as a flag
-
-#[test]
-fn test_match_all_end_of_options_not_matched() {
- // `--` should neither match as a flag nor take a value.
- let mut info = PickerArgInfo::new();
- info.set_long("name");
- let args = make_args(&[("--", 0)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert!(result.is_empty());
-}
-
-// `--` should never be consumed as a value by a named flag.
-
-#[test]
-fn test_match_all_named_does_not_consume_end_marker() {
- // `--flag` before `--`, but the next position IS `--`.
- // Only tag the flag, don't consume the end-of-options marker.
- let mut info = PickerArgInfo::new();
- info.set_long("flag");
- let args = make_args(&[("--flag", 0), ("--", 1), ("value", 2)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0], "should NOT consume -- as a value");
-}
-
-#[test]
-fn test_match_all_named_does_not_consume_end_marker_flag_before_end() {
- // Simulates: --flag -- you where -- is end-of-options.
- // The flag should be tagged but -- should NOT be consumed as its value.
- let mut info = PickerArgInfo::new();
- info.set_long("flag");
-
- let args = make_args(&[("--flag", 0), ("--", 1), ("you", 2)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(
- result,
- vec![0],
- "flag before --: only tag flag, leave -- for positional"
- );
-}
-
-#[test]
-fn test_match_all_flag_after_end_has_value() {
- // Flag after `--` should not match at all.
- let mut info = PickerArgInfo::new();
- info.set_long("flag");
- let args = make_args(&[("--", 0), ("--flag", 1), ("value", 2)]);
- let result = ArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert!(result.is_empty());
-}
diff --git a/mingling_picker/test/src/test/basic_test.rs b/mingling_picker/test/src/test/basic_test.rs
deleted file mode 100644
index 78b154e..0000000
--- a/mingling_picker/test/src/test/basic_test.rs
+++ /dev/null
@@ -1,223 +0,0 @@
-use mingling_picker::{IntoPicker, macros::arg};
-
-// Basic bool flag — present / absent
-
-#[test]
-fn test_bool_flag_present() {
- let args = vec!["--verbose"];
- let parsed = args
- .to_picker()
- .pick(&arg![verbose: bool])
- .or_default()
- .unwrap();
- assert_eq!(parsed, true);
-}
-
-#[test]
-fn test_bool_flag_absent() {
- let args: Vec<&str> = vec![];
- let parsed = args
- .to_picker()
- .pick(&arg![verbose: bool])
- .or_default()
- .unwrap();
- assert_eq!(parsed, false);
-}
-
-// Short flag — '-v'
-
-#[test]
-fn test_bool_short_flag_present() {
- let args = vec!["-v"];
- let parsed = args
- .to_picker()
- .pick(&arg![verbose: bool, 'v'])
- .or_default()
- .unwrap();
- assert_eq!(parsed, true);
-}
-
-// Multiple bool flags at once
-
-#[test]
-fn test_two_bool_flags_both_present() {
- // UNIX_STYLE now uses Kebab naming: `flag_a` → "flag-a" → --flag-a
- let args = vec!["--flag-a", "--flag-b"];
- let (a, b) = args
- .to_picker()
- .pick(&arg![flag_a: bool])
- .or_default()
- .pick(&arg![flag_b: bool])
- .or_default()
- .unwrap();
- assert_eq!(a, true);
- assert_eq!(b, true);
-}
-
-#[test]
-fn test_two_bool_flags_one_present() {
- let args = vec!["--flag-a"];
- let (a, b) = args
- .to_picker()
- .pick(&arg![flag_a: bool])
- .or_default()
- .pick(&arg![flag_b: bool])
- .or_default()
- .unwrap();
- assert_eq!(a, true);
- assert_eq!(b, false);
-}
-
-#[test]
-fn test_two_bool_flags_neither_present() {
- let args: Vec<&str> = vec![];
- let (a, b) = args
- .to_picker()
- .pick(&arg![flag_a: bool])
- .or_default()
- .pick(&arg![flag_b: bool])
- .or_default()
- .unwrap();
- assert_eq!(a, false);
- assert_eq!(b, false);
-}
-
-// Mixed short and long flags
-
-#[test]
-fn test_short_and_long_flags() {
- let args = vec!["-a", "--long-b"];
- let (a, b) = args
- .to_picker()
- .pick(&arg![flag_a: bool, 'a'])
- .or_default()
- .pick(&arg![long_b: bool])
- .or_default()
- .unwrap();
- assert_eq!(a, true);
- assert_eq!(b, true);
-}
-
-// Flags after `--` (end-of-options marker) should not be parsed.
-
-#[test]
-fn test_flag_after_end_of_options() {
- let args = vec!["--", "--verbose"];
- let parsed = args
- .to_picker()
- .pick(&arg![verbose: bool])
- .or_default()
- .unwrap();
- assert_eq!(parsed, false);
-}
-
-// Alias matching for bool flags
-
-#[test]
-fn test_bool_flag_with_alias() {
- let args = vec!["--cfg"];
- let parsed = args
- .to_picker()
- .pick(&arg![config: bool, "cfg"])
- .or_default()
- .unwrap();
- assert_eq!(parsed, true);
-}
-
-#[test]
-fn test_bool_flag_primary_name() {
- let args = vec!["--config"];
- let parsed = args
- .to_picker()
- .pick(&arg![config: bool, "cfg"])
- .or_default()
- .unwrap();
- assert_eq!(parsed, true);
-}
-
-// Short flag + alias for bool flag
-
-#[test]
-fn test_bool_flag_short_and_alias() {
- let args = vec!["-v"];
- let parsed = args
- .to_picker()
- .pick(&arg![verbose: bool, 'v', "cfg"])
- .or_default()
- .unwrap();
- assert_eq!(parsed, true);
-}
-
-// Default values: .or() / .or_default()
-
-#[test]
-fn test_or_default_without_args() {
- let args: Vec<&str> = vec![];
- let parsed = args
- .to_picker()
- .pick(&arg![verbose: bool])
- .or_default()
- .unwrap();
- assert_eq!(parsed, false);
-}
-
-#[test]
-fn test_or_custom_default() {
- let args: Vec<&str> = vec![];
- let parsed = args
- .to_picker()
- .pick(&arg![verbose: bool])
- .or(|| true)
- .unwrap();
- assert_eq!(parsed, true);
-}
-
-// to_result / to_option interface
-
-#[test]
-fn test_to_result_ok() {
- let args = vec!["--verbose"];
- let result = args
- .to_picker()
- .pick(&arg![verbose: bool])
- .or_default()
- .to_result();
- assert_eq!(result, Ok(true));
-}
-
-#[test]
-fn test_to_option_some() {
- let args = vec!["--verbose"];
- let opt = args
- .to_picker()
- .pick(&arg![verbose: bool])
- .or_default()
- .to_option();
- assert_eq!(opt, Some(true));
-}
-
-// Chain with_route passthrough
-
-#[test]
-fn test_with_route_chain() {
- let args = vec!["--flag"];
- let parsed = args
- .with_route::<String>()
- .pick(&arg![flag: bool])
- .or_default()
- .unwrap();
- assert_eq!(parsed, true);
-}
-
-// Unrelated flag should not match
-
-#[test]
-fn test_unrelated_flag_does_not_match() {
- let args = vec!["--other"];
- let parsed = args
- .to_picker()
- .pick(&arg![verbose: bool])
- .or_default()
- .unwrap();
- assert_eq!(parsed, false);
-}
diff --git a/mingling_picker/test/src/test/multi_arg_test.rs b/mingling_picker/test/src/test/multi_arg_test.rs
deleted file mode 100644
index 377357e..0000000
--- a/mingling_picker/test/src/test/multi_arg_test.rs
+++ /dev/null
@@ -1,181 +0,0 @@
-use mingling_picker::parselib::{Matcher, MultiArgMatcher, UNIX_STYLE};
-use mingling_picker::PickerArgInfo;
-
-use crate::make_args;
-
-// on_match_one — basic
-
-#[test]
-fn test_multi_one_finds_first_flag() {
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[("--other", 0), ("--val", 1), ("a", 2), ("b", 3)]);
- let result = MultiArgMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, Some(1));
-}
-
-#[test]
-fn test_multi_one_no_match() {
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[("--other", 0)]);
- let result = MultiArgMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, None);
-}
-
-// on_match_all — named, basic multi-value
-
-#[test]
-fn test_multi_all_flag_takes_all_values_until_next_flag() {
- // --val a b c → all three values belong to --val
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[("--val", 0), ("a", 1), ("b", 2), ("c", 3)]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1, 2, 3]);
-}
-
-#[test]
-fn test_multi_all_stops_at_next_flag() {
- // --val a b --other c d → only a,b belong to --val
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[("--val", 0), ("a", 1), ("b", 2), ("--other", 3), ("c", 4)]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1, 2]);
-}
-
-#[test]
-fn test_multi_all_flag_no_values() {
- // --val at end with no values → just the flag
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[("--val", 0)]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0]);
-}
-
-#[test]
-fn test_multi_all_empty() {
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = vec![];
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert!(result.is_empty());
-}
-
-// on_match_all — named, multiple occurrences of same flag
-
-#[test]
-fn test_multi_all_two_occurrences() {
- // --val a b --val c d → two groups
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[
- ("--val", 0), ("a", 1), ("b", 2),
- ("--val", 3), ("c", 4), ("d", 5),
- ]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1, 2, 3, 4, 5]);
-}
-
-#[test]
-fn test_multi_all_skips_non_matching_args() {
- // --val a --other b --val c → only --val groups
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[
- ("--val", 0), ("a", 1),
- ("--other", 2), ("b", 3),
- ("--val", 4), ("c", 5),
- ]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1, 4, 5]);
-}
-
-// on_match_all — named, eq mode
-
-#[test]
-fn test_multi_all_eq_mode() {
- // --val=a b → eq mode + one extra value
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[("--val=a", 0), ("b", 1)]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1]);
-}
-
-#[test]
-fn test_multi_all_eq_mode_no_extra() {
- // --val=a alone → just the eq flag
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[("--val=a", 0)]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0]);
-}
-
-#[test]
-fn test_multi_all_mixed_eq_and_regular() {
- // --val=a b --val c d
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[
- ("--val=a", 0), ("b", 1), ("--val", 2), ("c", 3), ("d", 4),
- ]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1, 2, 3, 4]);
-}
-
-// on_match_all — short flag
-
-#[test]
-fn test_multi_all_short_flag() {
- let mut info = PickerArgInfo::new();
- info.set_short('v');
- info.set_long("val");
- let args = make_args(&[("-v", 0), ("a", 1), ("b", 2)]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1, 2]);
-}
-
-// on_match_all — end-of-options marker
-
-#[test]
-fn test_multi_all_stops_at_end_of_options() {
- // --val a -- b → stops before `--`
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[("--val", 0), ("a", 1), ("--", 2), ("b", 3)]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1]);
-}
-
-#[test]
-fn test_multi_all_flag_after_end_ignored() {
- let mut info = PickerArgInfo::new();
- info.set_long("val");
- let args = make_args(&[("--", 0), ("--val", 1), ("a", 2)]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert!(result.is_empty());
-}
-
-// on_match_all — positional
-
-#[test]
-fn test_multi_all_positional() {
- let mut info = PickerArgInfo::new();
- info.set_positional(true);
- let args = make_args(&[("a.txt", 0), ("b.txt", 1)]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0, 1]);
-}
-
-#[test]
-fn test_multi_all_positional_stops_at_end_of_options() {
- let mut info = PickerArgInfo::new();
- info.set_positional(true);
- let args = make_args(&[("a.txt", 0), ("--", 1), ("b.txt", 2)]);
- let result = MultiArgMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![0]);
-}
diff --git a/mingling_picker/test/src/test/multi_value_test.rs b/mingling_picker/test/src/test/multi_value_test.rs
deleted file mode 100644
index 0f56517..0000000
--- a/mingling_picker/test/src/test/multi_value_test.rs
+++ /dev/null
@@ -1,66 +0,0 @@
-use mingling_picker::value::{Flag, VecUntil};
-use mingling_picker::{IntoPicker, macros::arg};
-
-#[test]
-fn test_vec_until_i16_named() {
- let (nums, rest): (VecUntil<i16>, Flag) = vec!["--nums", "1", "2", "3", "abc"]
- .to_picker()
- .pick(&arg![nums: VecUntil<i16>])
- .pick(&arg![rest: Flag])
- .unwrap();
- assert_eq!(*nums, vec![1i16, 2, 3]);
- assert_eq!(rest, Flag::Inactive);
-}
-
-#[test]
-fn test_vec_until_i16_stops_at_non_number() {
- let nums: VecUntil<i16> = vec!["--nums", "42", "abc", "100"]
- .to_picker()
- .pick(&arg![nums: VecUntil<i16>])
- .unwrap();
- assert_eq!(*nums, vec![42i16]);
-}
-
-#[test]
-fn test_vec_until_i16_empty() {
- let nums: VecUntil<i16> = vec!["--nums"]
- .to_picker()
- .pick(&arg![nums: VecUntil<i16>])
- .unwrap();
- assert!(nums.is_empty());
-}
-
-#[test]
-fn test_vec_until_i16_stops_at_next_flag() {
- let nums: VecUntil<i16> = vec!["--nums", "1", "2", "--other", "3"]
- .to_picker()
- .pick(&arg![nums: VecUntil<i16>])
- .unwrap();
- assert_eq!(*nums, vec![1i16, 2]);
-}
-
-#[test]
-fn test_vec_until_i16_stops_at_end_of_options() {
- let nums: VecUntil<i16> = vec!["--nums", "1", "2", "--", "3"]
- .to_picker()
- .pick(&arg![nums: VecUntil<i16>])
- .unwrap();
- assert_eq!(*nums, vec![1i16, 2]);
-}
-
-// Two VecUntil<T> with different boundary behaviours
-
-#[test]
-fn test_vec_until_f64_and_i16_positional() {
- // Both are positional VecUntil. f64 takes all valid floats,
- // i16 takes what's left. But note: "1", "2", "3" are also valid
- // f64 values, so f64's check_boundary never fires — it consumes
- // everything, and i16 gets nothing.
- let (floats, ints): (VecUntil<f64>, VecUntil<i16>) = vec!["1.5", "2.5", "3.5", "1", "2", "3"]
- .to_picker()
- .pick(&arg![VecUntil<f64>])
- .pick(&arg![VecUntil<i16>])
- .unwrap();
- assert_eq!(*floats, vec![1.5, 2.5, 3.5]);
- assert_eq!(*ints, vec![1i16, 2, 3]);
-}
diff --git a/mingling_picker/test/src/test/pos_matcher_test.rs b/mingling_picker/test/src/test/pos_matcher_test.rs
deleted file mode 100644
index b5319f5..0000000
--- a/mingling_picker/test/src/test/pos_matcher_test.rs
+++ /dev/null
@@ -1,141 +0,0 @@
-use mingling_picker::PickerArgInfo;
-use mingling_picker::parselib::{MaskedArg, Matcher, PositionalMatcher, UNIX_STYLE, WINDOWS_STYLE};
-
-fn make_args<'a>(pairs: &'a [(&'a str, usize)]) -> Vec<MaskedArg<'a>> {
- pairs
- .iter()
- .map(|&(raw, idx)| MaskedArg { raw, raw_idx: idx })
- .collect()
-}
-
-// on_match_one — basic
-
-#[test]
-fn test_pos_one_takes_first_non_flag() {
- let info = PickerArgInfo::new();
- let args = make_args(&[("--verbose", 0), ("file.txt", 1)]);
- let result = PositionalMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, Some(1));
-}
-
-#[test]
-fn test_pos_one_takes_first_if_no_flag() {
- let info = PickerArgInfo::new();
- let args = make_args(&[("file.txt", 0)]);
- let result = PositionalMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, Some(0));
-}
-
-#[test]
-fn test_pos_one_all_flags_returns_none() {
- let info = PickerArgInfo::new();
- let args = make_args(&[("--verbose", 0), ("--name", 1)]);
- let result = PositionalMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, None);
-}
-
-#[test]
-fn test_pos_one_empty_returns_none() {
- let info = PickerArgInfo::new();
- let args = vec![];
- let result = PositionalMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, None);
-}
-
-// on_match_one — after `--`
-
-#[test]
-fn test_pos_one_after_end_takes_even_flag_like() {
- // After `--`, accept everything including `--verbose`.
- let info = PickerArgInfo::new();
- let args = make_args(&[("--", 0), ("--verbose", 1), ("file.txt", 2)]);
- let result = PositionalMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, Some(1));
-}
-
-#[test]
-fn test_pos_one_only_end_returns_none() {
- let info = PickerArgInfo::new();
- let args = make_args(&[("--", 0)]);
- let result = PositionalMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, None);
-}
-
-// on_match_one — Windows style prefix
-
-#[test]
-fn test_pos_one_windows_skips_slash_prefix() {
- let info = PickerArgInfo::new();
- let args = make_args(&[("/Verbose", 0), ("file.txt", 1)]);
- let result = PositionalMatcher::on_match_one(&args, &WINDOWS_STYLE, &info);
- assert_eq!(result, Some(1));
-}
-
-// on_match_all — basic
-
-#[test]
-fn test_pos_all_collects_non_flags() {
- let info = PickerArgInfo::new();
- let args = make_args(&[("--verbose", 0), ("a.txt", 1), ("--name", 2), ("b.txt", 3)]);
- let result = PositionalMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![1, 3]);
-}
-
-#[test]
-fn test_pos_all_only_flags_returns_empty() {
- let info = PickerArgInfo::new();
- let args = make_args(&[("--a", 0), ("--b", 1)]);
- let result = PositionalMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert!(result.is_empty());
-}
-
-#[test]
-fn test_pos_all_empty() {
- let info = PickerArgInfo::new();
- let args = vec![];
- let result = PositionalMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert!(result.is_empty());
-}
-
-// on_match_all — after `--`
-
-#[test]
-fn test_pos_all_after_end_accepts_everything() {
- // After `--`, even `--verbose` is accepted as positional.
- let info = PickerArgInfo::new();
- let args = make_args(&[
- ("--verbose", 0),
- ("a.txt", 1),
- ("--", 2),
- ("--flag", 3),
- ("b.txt", 4),
- ]);
- let result = PositionalMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![1, 3, 4]);
-}
-
-#[test]
-fn test_pos_all_only_after_end() {
- let info = PickerArgInfo::new();
- let args = make_args(&[("--", 0), ("arg1", 1), ("--arg2", 2)]);
- let result = PositionalMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- assert_eq!(result, vec![1, 2]);
-}
-
-// on_match_all — mixed before/after `--`
-
-#[test]
-fn test_pos_all_mixed() {
- let info = PickerArgInfo::new();
- let args = make_args(&[
- ("infile", 0),
- ("--verbose", 1),
- ("--", 2),
- ("outfile", 3),
- ("--extra", 4),
- ]);
- let result = PositionalMatcher::on_match_all(&args, &UNIX_STYLE, &info);
- // Before `--`: "infile" (skip --verbose).
- // After `--`: everything — "outfile", "--extra".
- assert_eq!(result, vec![0, 3, 4]);
-}
diff --git a/mingling_picker/test/src/test/priority_test.rs b/mingling_picker/test/src/test/priority_test.rs
deleted file mode 100644
index 8179389..0000000
--- a/mingling_picker/test/src/test/priority_test.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use mingling_picker::value::Flag;
-use mingling_picker::{IntoPicker, macros::arg};
-
-// Same flag name, different Pickable types
-//
-// PickerArgAttr priority: Flag < Single
-// So Single and Multi should parse BEFORE Flag when sharing
-// the same flag name.
-
-#[test]
-fn test_single_takes_flag_when_sharing_name() {
- // --name Alice: String consumes it, Flag sees nothing.
- let (name, verbose): (String, Flag) = vec!["--name", "Alice"]
- .to_picker()
- .pick(&arg![name: String]) // Single, parsed first
- .pick(&arg![name: Flag]) // Flag, parsed second, nothing left
- .unwrap();
- assert_eq!(name, "Alice");
- assert_eq!(verbose, Flag::Inactive);
-}
-
-#[test]
-fn test_flag_only_triggers_when_single_missing() {
- // --verbose: only Flag matches, String gets default.
- let (name, verbose): (String, Flag) = vec!["--verbose"]
- .to_picker()
- .pick(&arg![name: String]) // Single, no match → default ""
- .or_default()
- .pick(&arg![name: Flag]) // Flag, no --name in args → Inactive
- .unwrap();
- assert_eq!(name, "");
- assert_eq!(verbose, Flag::Inactive);
-}
-
-#[test]
-fn test_flag_gets_leftovers_after_single_consumes_value() {
- // --name Alice --name: String takes "--name Alice", Flag takes "--name".
- let (name, verbose): (String, Flag) = vec!["--name", "Alice", "--name"]
- .to_picker()
- .pick(&arg![name: String]) // Single: tag [0, 1], consumes --name Alice
- .pick(&arg![name: Flag]) // Flag: tags position 2 (--name)
- .unwrap();
- assert_eq!(name, "Alice");
- assert_eq!(
- verbose,
- Flag::Active,
- "Flag should see --name at position 2 after String consumed positions 0-1"
- );
-}
-
-#[test]
-fn test_short_flag_sharing_same_letter() {
- // -n Alice: String takes it, Flag misses.
- let (name, verbose): (String, Flag) = vec!["-n", "Alice"]
- .to_picker()
- .pick(&arg![name: String, 'n']) // Single, parsed first
- .pick(&arg![name: Flag, 'n']) // Flag, parsed second
- .unwrap();
- assert_eq!(name, "Alice");
- assert_eq!(verbose, Flag::Inactive);
-}
-
-#[test]
-fn test_flag_captures_remaining_after_single_partial_consume() {
- // --name Alice --verbose: String takes --name Alice, Flag takes --verbose.
- let (name, verbose): (String, Flag) = vec!["--name", "Alice", "--verbose"]
- .to_picker()
- .pick(&arg![name: String]) // Single: tag [0, 1]
- .pick(&arg![verbose: Flag]) // Flag: tag [2]
- .unwrap();
- assert_eq!(name, "Alice");
- assert_eq!(verbose, Flag::Active);
-}
-
-#[test]
-fn test_single_skips_already_claimed_positions() {
- // --verbose --name Alice: Flag takes --verbose, String takes --name Alice.
- let (verbose, name): (Flag, String) = vec!["--verbose", "--name", "Alice"]
- .to_picker()
- .pick(&arg![verbose: Flag]) // Flag: tag [0]
- .pick(&arg![name: String]) // Single: tag [1, 2]
- .unwrap();
- assert_eq!(verbose, Flag::Active);
- assert_eq!(name, "Alice");
-}
diff --git a/mingling_picker/test/src/test/route_test.rs b/mingling_picker/test/src/test/route_test.rs
deleted file mode 100644
index 9261db1..0000000
--- a/mingling_picker/test/src/test/route_test.rs
+++ /dev/null
@@ -1,200 +0,0 @@
-use mingling_picker::{IntoPicker, macros::arg};
-
-// Route mechanism — or_route
-
-#[test]
-fn test_or_route_triggered_to_result() {
- // flag not present and no default value → route triggered → Err
- let args: Vec<&str> = vec![];
- let result: Result<bool, &'static str> = args
- .with_route::<&'static str>()
- .pick(&arg![verbose: bool])
- .or_route(|| "missing_verbose")
- .to_result();
- assert_eq!(result, Err("missing_verbose"));
-}
-
-#[test]
-fn test_or_route_not_triggered_when_flag_present() {
- // flag present → route not triggered → Ok
- let args = vec!["--verbose"];
- let result: Result<bool, &'static str> = args
- .with_route::<&'static str>()
- .pick(&arg![verbose: bool])
- .or_route(|| "missing_verbose")
- .to_result();
- assert_eq!(result, Ok(true));
-}
-
-#[test]
-fn test_or_default_priority_over_or_route() {
- // or_default takes priority over or_route: even if the flag is absent,
- // having a default prevents the route from being triggered
- let args: Vec<&str> = vec![];
- let result: Result<bool, &'static str> = args
- .with_route::<&'static str>()
- .pick(&arg![verbose: bool])
- .or_default()
- .or_route(|| "should_not_reach")
- .to_result();
- assert_eq!(result, Ok(false));
-}
-
-#[test]
-fn test_or_route_to_option_returns_none() {
- // When route is triggered, to_option returns None
- let args: Vec<&str> = vec![];
- let opt: Option<bool> = args
- .with_route::<&'static str>()
- .pick(&arg![verbose: bool])
- .or_route(|| "missing")
- .to_option();
- assert_eq!(opt, None);
-}
-
-#[test]
-#[should_panic(expected = "called `Option::unwrap()` on a `None` value")]
-fn test_or_route_unwrap_panics() {
- let args: Vec<&str> = vec![];
- args.with_route::<&'static str>()
- .pick(&arg![verbose: bool])
- .or_route(|| "missing")
- .unwrap();
-}
-
-#[test]
-fn test_or_route_with_string_route() {
- // Route type is String
- let args: Vec<&str> = vec![];
- let result: Result<bool, String> = args
- .with_route::<String>()
- .pick(&arg![verbose: bool])
- .or_route(|| "route_hit".to_string())
- .to_result();
- assert_eq!(result, Err("route_hit".to_string()));
-}
-
-#[test]
-fn test_or_route_with_custom_enum() {
- // Route type is a custom enum
- #[derive(Debug, Clone, Copy, PartialEq, Eq)]
- #[allow(dead_code)]
- enum Redirect {
- Help,
- Version,
- }
-
- let args: Vec<&str> = vec![];
- let result: Result<bool, Redirect> = args
- .with_route::<Redirect>()
- .pick(&arg![verbose: bool])
- .or_route(|| Redirect::Help)
- .to_result();
- assert_eq!(result, Err(Redirect::Help));
-}
-
-// Route mechanism — multiple flags
-
-#[test]
-fn test_two_flags_first_missing_triggers_route() {
- // Both flags missing; first has or_route → Err("first");
- // first route wins (first-checked, first-triggered)
- let args: Vec<&str> = vec![];
- let result: Result<(bool, bool), &'static str> = args
- .with_route::<&'static str>()
- .pick(&arg![flag_a: bool])
- .or_route(|| "first_missing")
- .pick(&arg![flag_b: bool])
- .or_route(|| "second_missing")
- .to_result();
- assert_eq!(result, Err("first_missing"));
-}
-
-#[test]
-fn test_two_flags_second_missing_triggers_route() {
- // First has or_default (no route triggered), second missing with or_route → Err("second")
- let args: Vec<&str> = vec![];
- let result: Result<(bool, bool), &'static str> = args
- .with_route::<&'static str>()
- .pick(&arg![flag_a: bool])
- .or_default()
- .pick(&arg![flag_b: bool])
- .or_route(|| "second_missing")
- .to_result();
- assert_eq!(result, Err("second_missing"));
-}
-
-#[test]
-fn test_two_flags_both_present_route_not_triggered() {
- // Both flags present → route not triggered → Ok((true, true))
- let args = vec!["--flag-a", "--flag-b"];
- let result: Result<(bool, bool), &'static str> = args
- .with_route::<&'static str>()
- .pick(&arg![flag_a: bool])
- .or_route(|| "first_missing")
- .pick(&arg![flag_b: bool])
- .or_route(|| "second_missing")
- .to_result();
- assert_eq!(result, Ok((true, true)));
-}
-
-#[test]
-fn test_two_flags_first_missing_no_route_second_has_route() {
- // First missing but has no or_route, second missing with or_route → Err("second")
- // Note: the first has neither route nor default, so pick failure does not modify error_route
- let args: Vec<&str> = vec![];
- let result: Result<(bool, bool), &'static str> = args
- .with_route::<&'static str>()
- .pick(&arg![flag_a: bool])
- // No or_default, no or_route either
- .pick(&arg![flag_b: bool])
- .or_route(|| "second_missing")
- .to_result();
- assert_eq!(result, Err("second_missing"));
-}
-
-#[test]
-fn test_two_flags_only_second_has_default() {
- // First is missing with no default/route (v1=NotFound), second is missing but has or_default
- // Use unpack to check both results
- let args: Vec<&str> = vec![];
- let (a, b) = args
- .to_picker()
- .pick(&arg![flag_a: bool])
- .pick(&arg![flag_b: bool])
- .or_default()
- .unpack();
- assert_eq!(a, None);
- assert_eq!(b, Some(false));
-}
-
-// Route with with_route
-
-#[test]
-fn test_with_route_and_or_route() {
- // with_route::&lt;String&gt;() sets the Route type + or_route triggers
- let args: Vec<&str> = vec![];
- let result: Result<bool, String> = args
- .with_route::<String>()
- .pick(&arg![verbose: bool])
- .or_route(|| "redirected".to_string())
- .to_result();
- assert_eq!(result, Err("redirected".to_string()));
-}
-
-#[test]
-fn test_with_route_type_switch() {
- // with_route switching Route type clears old route_$ and error_route
- let args: Vec<&str> = vec![];
- let result: Result<(bool, bool), i32> = args
- .with_route::<String>()
- .pick(&arg![verbose: bool])
- .or_route(|| "string_route".to_string())
- .with_route::<i32>()
- .pick(&arg![other: bool])
- .or_route(|| -1)
- .to_result();
- // The first flag is missing, but its or_route is cleared when with_route switches (route_$ = None)
- // The second flag is missing and has or_route → Err(-1)
- assert_eq!(result, Err(-1));
-}
diff --git a/mingling_picker/test/src/test/style_test.rs b/mingling_picker/test/src/test/style_test.rs
deleted file mode 100644
index 3c337b9..0000000
--- a/mingling_picker/test/src/test/style_test.rs
+++ /dev/null
@@ -1,300 +0,0 @@
-use mingling_picker::PickerArgInfo;
-use mingling_picker::parselib::{
- FlagMatcher, Matcher, POWERSHELL_STYLE, ParserStyle, ParserStyleNamingCase, UNIX_STYLE,
- WINDOWS_STYLE, build_possible_flags,
-};
-
-use crate::make_masked;
-
-// Style: formatting utilities
-
-#[test]
-fn test_unix_style_flag_string() {
- assert_eq!(UNIX_STYLE.flag_string('v'), "-v");
- assert_eq!(UNIX_STYLE.flag_string("verbose"), "--verbose");
-}
-
-#[test]
-fn test_windows_style_flag_string() {
- assert_eq!(WINDOWS_STYLE.flag_string('v'), "/v");
- assert_eq!(WINDOWS_STYLE.flag_string("verbose"), "/verbose");
-}
-
-#[test]
-fn test_powershell_style_flag_string() {
- assert_eq!(POWERSHELL_STYLE.flag_string('v'), "-v");
- assert_eq!(POWERSHELL_STYLE.flag_string("Verbose"), "-Verbose");
-}
-
-#[test]
-fn test_build_possible_flags_windows() {
- // Build PickerArgInfo from a flag definition: `verbose: bool`
- let mut info = PickerArgInfo::new();
- info.set_long("verbose");
- let flags = build_possible_flags(&WINDOWS_STYLE, &info);
- assert_eq!(flags, vec!["/Verbose"]);
-}
-
-#[test]
-fn test_build_possible_flags_with_short_and_alias() {
- let mut info = PickerArgInfo::new();
- info.set_short('n');
- info.set_long("name");
- info.set_alias(vec!["nickname"]);
- let flags = build_possible_flags(&UNIX_STYLE, &info);
- assert_eq!(flags, vec!["-n", "--name", "--nickname"]);
-}
-
-// Style: matching with different styles via Matcher trait
-
-#[test]
-fn test_windows_style_match() {
- // Windows style: /verbose (case insensitive)
- let mut info = PickerArgInfo::new();
- info.set_long("verbose");
-
- let args = vec![make_masked("/verbose", 0)];
- let result = FlagMatcher::on_match_one(&args, &WINDOWS_STYLE, &info);
- assert_eq!(result, Some(0));
-}
-
-#[test]
-fn test_windows_style_match_case_insensitive() {
- // Windows style is case-insensitive: /VERBOSE should match "verbose"
- let mut info = PickerArgInfo::new();
- info.set_long("verbose");
-
- let args = vec![make_masked("/VERBOSE", 0)];
- let result = FlagMatcher::on_match_one(&args, &WINDOWS_STYLE, &info);
- assert_eq!(result, Some(0));
-}
-
-#[test]
-fn test_windows_style_no_match_on_unrelated_flag() {
- // Different flag should not match
- let mut info = PickerArgInfo::new();
- info.set_long("verbose");
-
- let args = vec![make_masked("/output", 0)];
- let result = FlagMatcher::on_match_one(&args, &WINDOWS_STYLE, &info);
- assert_eq!(result, None);
-}
-
-#[test]
-fn test_powershell_style_match() {
- // PowerShell style: -Verbose (case insensitive)
- let mut info = PickerArgInfo::new();
- info.set_long("Verbose");
-
- let args = vec![make_masked("-Verbose", 0)];
- let result = FlagMatcher::on_match_one(&args, &POWERSHELL_STYLE, &info);
- assert_eq!(result, Some(0));
-}
-
-#[test]
-fn test_powershell_style_match_case_insensitive() {
- let mut info = PickerArgInfo::new();
- info.set_long("Verbose");
-
- let args = vec![make_masked("-VERBOSE", 0)];
- let result = FlagMatcher::on_match_one(&args, &POWERSHELL_STYLE, &info);
- assert_eq!(result, Some(0));
-}
-
-#[test]
-fn test_unix_style_case_sensitive_no_match() {
- // UNIX style is case-sensitive: --VERBOSE should NOT match --verbose
- let mut info = PickerArgInfo::new();
- info.set_long("verbose");
-
- let args = vec![make_masked("--VERBOSE", 0)];
- let result = FlagMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(result, None);
-}
-
-#[test]
-fn test_windows_style_match_all() {
- // on_match_all should find all matching flags
- let mut info = PickerArgInfo::new();
- info.set_long("verbose");
- info.set_short('v');
-
- let args = vec![
- make_masked("/v", 0),
- make_masked("/output", 1),
- make_masked("/VERBOSE", 2),
- ];
- let result = FlagMatcher::on_match_all(&args, &WINDOWS_STYLE, &info);
- assert_eq!(result, vec![0, 2]);
-}
-
-#[test]
-fn test_windows_style_match_after_end_of_options() {
- // end_of_options is always "--" regardless of style
- // Flags after -- should not match
- let mut info = PickerArgInfo::new();
- info.set_long("verbose");
-
- let args = vec![
- make_masked("/verbose", 0),
- make_masked("--", 1),
- make_masked("/verbose", 2),
- ];
- let result = FlagMatcher::on_match_all(&args, &WINDOWS_STYLE, &info);
- // end_of_options is always "--" regardless of style
- assert_eq!(result, vec![0]);
-}
-
-// Naming case conversion
-
-/// A Unix-like style with kebab-case naming.
-const KEBAB_STYLE: ParserStyle<'static> = ParserStyle {
- naming_case: ParserStyleNamingCase::Kebab,
- ..UNIX_STYLE
-};
-
-#[test]
-fn test_kebab_case_naming_for_multiword_flag() {
- // flag name `flag_a` → Kebab → `flag-a` → `--flag-a`
- let mut info = PickerArgInfo::new();
- info.set_long("flag_a");
-
- let args = vec![make_masked("--flag-a", 0)];
- let result = FlagMatcher::on_match_one(&args, &KEBAB_STYLE, &info);
- assert_eq!(
- result,
- Some(0),
- "--flag-a should match flag_a via kebab-case conversion"
- );
-}
-
-#[test]
-fn test_snake_case_should_not_match_as_long_flag() {
- // With kebab naming, `--flag_a` (snake) should NOT match `flag_a`
- let mut info = PickerArgInfo::new();
- info.set_long("flag_a");
-
- let args = vec![make_masked("--flag_a", 0)];
- let result = FlagMatcher::on_match_one(&args, &KEBAB_STYLE, &info);
- assert_eq!(
- result, None,
- "--flag_a should NOT match in kebab-style context"
- );
-}
-
-#[test]
-fn test_kebab_case_naming_for_unix_style() {
- // UNIX_STYLE now uses Kebab case: `flag_a` → `flag-a` → `--flag-a`
- let mut info = PickerArgInfo::new();
- info.set_long("flag_a");
-
- let args = vec![make_masked("--flag-a", 0)];
- let result = FlagMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(
- result,
- Some(0),
- "--flag-a should match with kebab-style UNIX_STYLE"
- );
-}
-
-#[test]
-fn test_snake_case_rejected_by_unix_style() {
- // UNIX_STYLE uses Kebab: `--flag_a` (snake) should NOT match
- let mut info = PickerArgInfo::new();
- info.set_long("flag_a");
-
- let args = vec![make_masked("--flag_a", 0)];
- let result = FlagMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(
- result, None,
- "--flag_a should NOT match under kebab-style UNIX_STYLE"
- );
-}
-
-#[test]
-fn test_powershell_pascal_case_naming() {
- // POWERSHELL_STYLE uses Pascal case: `verbose` → `Verbose` → `-Verbose`
- let mut info = PickerArgInfo::new();
- info.set_long("verbose");
-
- let args = vec![make_masked("-Verbose", 0)];
- let result = FlagMatcher::on_match_one(&args, &POWERSHELL_STYLE, &info);
- assert_eq!(
- result,
- Some(0),
- "-Verbose should match verbose via Pascal case"
- );
-}
-
-#[test]
-fn test_flag_naming_kebab_matches_my_name() {
- // UNIX_STYLE now uses Kebab: `my_name` → `my-name` → `--my-name`
- let mut info = PickerArgInfo::new();
- info.set_long("my_name");
-
- let args = vec![make_masked("--my-name", 0)];
- let result = FlagMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(
- result,
- Some(0),
- "--my-name should match via kebab conversion"
- );
-}
-
-#[test]
-fn test_flag_naming_kebab_rejects_my_name_underscore() {
- // Kebab style: `--my_name` (snake) should NOT match
- let mut info = PickerArgInfo::new();
- info.set_long("my_name");
-
- let args = vec![make_masked("--my_name", 0)];
- let result = FlagMatcher::on_match_one(&args, &UNIX_STYLE, &info);
- assert_eq!(
- result, None,
- "--my_name should NOT match under kebab-style UNIX_STYLE"
- );
-}
-
-#[test]
-fn test_flag_naming_pascal_matches_my_name() {
- // `my_name` under Pascal → `MyName` → `-MyName`
- let mut info = PickerArgInfo::new();
- info.set_long("my_name");
-
- let args = vec![make_masked("-MyName", 0)];
- let result = FlagMatcher::on_match_one(&args, &POWERSHELL_STYLE, &info);
- assert_eq!(
- result,
- Some(0),
- "-MyName should match via Pascal conversion"
- );
-}
-
-#[test]
-fn test_flag_naming_pascal_matches_lowercase() {
- // PowerShell is case-insensitive: `-myname` should also match
- let mut info = PickerArgInfo::new();
- info.set_long("my_name");
-
- let args = vec![make_masked("-myname", 0)];
- let result = FlagMatcher::on_match_one(&args, &POWERSHELL_STYLE, &info);
- assert_eq!(
- result,
- Some(0),
- "-myname (lowercase) should match via case-insensitive Pascal"
- );
-}
-
-#[test]
-fn test_flag_naming_pascal_rejects_my_name_underscore() {
- // Pascal style: `-my_name` (underscore) should NOT match
- let mut info = PickerArgInfo::new();
- info.set_long("my_name");
-
- let args = vec![make_masked("-my_name", 0)];
- let result = FlagMatcher::on_match_one(&args, &POWERSHELL_STYLE, &info);
- assert_eq!(
- result, None,
- "-my_name should NOT match under Pascal-style naming"
- );
-}
diff --git a/mingling_picker/test/src/test/value_flag_test.rs b/mingling_picker/test/src/test/value_flag_test.rs
deleted file mode 100644
index d267a27..0000000
--- a/mingling_picker/test/src/test/value_flag_test.rs
+++ /dev/null
@@ -1,174 +0,0 @@
-use mingling_picker::value::Flag;
-use mingling_picker::{IntoPicker, macros::arg};
-
-// Basic Flag — present / absent
-
-#[test]
-fn test_flag_present() {
- let flag: Flag = vec!["--verbose"]
- .to_picker()
- .pick(&arg![verbose: Flag])
- .unwrap();
- assert_eq!(flag, Flag::Active);
-}
-
-#[test]
-fn test_flag_absent_returns_inactive() {
- // Unlike bool, Flag::pick returns Parsed(Inactive) when no match is found,
- // so or_default() is NOT required — unwrap() works directly.
- let flag: Flag = Vec::<&str>::new()
- .to_picker()
- .pick(&arg![verbose: Flag])
- .unwrap();
- assert_eq!(flag, Flag::Inactive);
-}
-
-// Short Flag
-
-#[test]
-fn test_flag_short_present() {
- let flag: Flag = vec!["-v"]
- .to_picker()
- .pick(&arg![verbose: Flag, 'v'])
- .unwrap();
- assert_eq!(flag, Flag::Active);
-}
-
-// Multiple Flags
-
-#[test]
-fn test_two_flags_both_present() {
- let (a, b): (Flag, Flag) = vec!["--flag-a", "--flag-b"]
- .to_picker()
- .pick(&arg![flag_a: Flag])
- .pick(&arg![flag_b: Flag])
- .unwrap();
- assert_eq!(a, Flag::Active);
- assert_eq!(b, Flag::Active);
-}
-
-#[test]
-fn test_two_flags_one_present() {
- let (a, b): (Flag, Flag) = vec!["--flag-a"]
- .to_picker()
- .pick(&arg![flag_a: Flag])
- .pick(&arg![flag_b: Flag])
- .unwrap();
- assert_eq!(a, Flag::Active);
- assert_eq!(b, Flag::Inactive);
-}
-
-#[test]
-fn test_two_flags_neither_present() {
- let (a, b): (Flag, Flag) = Vec::<&str>::new()
- .to_picker()
- .pick(&arg![flag_a: Flag])
- .pick(&arg![flag_b: Flag])
- .unwrap();
- assert_eq!(a, Flag::Inactive);
- assert_eq!(b, Flag::Inactive);
-}
-
-// After `--` (end-of-options)
-
-#[test]
-fn test_flag_after_end_of_options() {
- let flag: Flag = vec!["--", "--verbose"]
- .to_picker()
- .pick(&arg![verbose: Flag])
- .unwrap();
- assert_eq!(flag, Flag::Inactive);
-}
-
-// Alias
-
-#[test]
-fn test_flag_with_alias() {
- let flag: Flag = vec!["--cfg"]
- .to_picker()
- .pick(&arg![config: Flag, "cfg"])
- .unwrap();
- assert_eq!(flag, Flag::Active);
-}
-
-#[test]
-fn test_flag_primary_name() {
- let flag: Flag = vec!["--config"]
- .to_picker()
- .pick(&arg![config: Flag, "cfg"])
- .unwrap();
- assert_eq!(flag, Flag::Active);
-}
-
-// Unrelated flag should not match
-
-#[test]
-fn test_unrelated_flag_does_not_match() {
- let flag: Flag = vec!["--other"]
- .to_picker()
- .pick(&arg![verbose: Flag])
- .unwrap();
- assert_eq!(flag, Flag::Inactive);
-}
-
-// to_result / to_option
-
-#[test]
-fn test_flag_to_result() {
- let result: Result<Flag, ()> = vec!["--verbose"]
- .to_picker()
- .pick(&arg![verbose: Flag])
- .to_result();
- assert_eq!(result, Ok(Flag::Active));
-}
-
-#[test]
-fn test_flag_to_option() {
- let opt: Option<Flag> = vec!["--verbose"]
- .to_picker()
- .pick(&arg![verbose: Flag])
- .to_option();
- assert_eq!(opt, Some(Flag::Active));
-}
-
-// Bool conversions
-
-#[test]
-fn test_flag_converts_to_bool() {
- let flag = Flag::Active;
- assert!(bool::from(flag));
-
- let flag = Flag::Inactive;
- assert!(!bool::from(flag));
-}
-
-#[test]
-fn test_flag_from_bool() {
- assert_eq!(Flag::from(true), Flag::Active);
- assert_eq!(Flag::from(false), Flag::Inactive);
-}
-
-#[test]
-fn test_flag_deref_to_bool() {
- let active = Flag::Active;
- assert!(*active);
-
- let inactive = Flag::Inactive;
- assert!(!*inactive);
-}
-
-// Flag never triggers route (unlike bool)
-//
-// Flag::pick always returns Parsed, so the fallback chain
-// (default → route) is never entered.
-
-#[test]
-fn test_flag_absent_does_not_trigger_route() {
- // Even without or_default / or_route, absent flag returns Inactive, not a route
- let result: Result<Flag, &str> = Vec::<&str>::new()
- .with_route::<&str>()
- .pick(&arg![verbose: Flag])
- .or_route(|| "should_not_fire")
- .to_result();
- assert_eq!(result, Ok(Flag::Inactive));
-}
diff --git a/mingling_picker/test/src/test/value_string_test.rs b/mingling_picker/test/src/test/value_string_test.rs
deleted file mode 100644
index b1e5c0b..0000000
--- a/mingling_picker/test/src/test/value_string_test.rs
+++ /dev/null
@@ -1,171 +0,0 @@
-use mingling_picker::{IntoPicker, macros::arg};
-
-// Basic named String — present / absent
-
-#[test]
-fn test_string_named_present() {
- let val: String = vec!["--name", "Alice"]
- .to_picker()
- .pick(&arg![name: String])
- .or_default()
- .unwrap();
- assert_eq!(val, "Alice");
-}
-
-#[test]
-fn test_string_named_absent_uses_default() {
- let val: String = Vec::<&str>::new()
- .to_picker()
- .pick(&arg![name: String])
- .or_default()
- .unwrap();
- assert_eq!(val, "");
-}
-
-// Named String — eq mode
-
-#[test]
-fn test_string_named_eq_mode() {
- let val: String = vec!["--name=Alice"]
- .to_picker()
- .pick(&arg![name: String])
- .or_default()
- .unwrap();
- assert_eq!(val, "Alice");
-}
-
-// Named String — short flag
-
-#[test]
-fn test_string_named_short_flag() {
- let val: String = vec!["-n", "Alice"]
- .to_picker()
- .pick(&arg![name: String, 'n'])
- .or_default()
- .unwrap();
- assert_eq!(val, "Alice");
-}
-
-// Named String — no value after flag
-
-#[test]
-fn test_string_named_missing_value_triggers_default() {
- // --name at end with no following arg → pick returns NotFound → or_default gives ""
- let val: String = vec!["--name"]
- .to_picker()
- .pick(&arg![name: String])
- .or_default()
- .unwrap();
- assert_eq!(val, "");
-}
-
-// Positional String
-
-#[test]
-fn test_string_positional() {
- let val: String = vec!["file.txt"]
- .to_picker()
- .pick(&arg![String])
- .or_default()
- .unwrap();
- assert_eq!(val, "file.txt");
-}
-
-#[test]
-fn test_string_positional_takes_first() {
- let val: String = vec!["first", "second"]
- .to_picker()
- .pick(&arg![String])
- .or_default()
- .unwrap();
- assert_eq!(val, "first");
-}
-
-// Multiple occurrences (Single only tags one occurrence per Parseable)
-
-#[test]
-fn test_string_two_named_flags() {
- let (a, b): (String, String) = vec!["--name", "Alice", "--greeting", "Hello"]
- .to_picker()
- .pick(&arg![name: String])
- .or_default()
- .pick(&arg![greeting: String])
- .or_default()
- .unwrap();
- assert_eq!(a, "Alice");
- assert_eq!(b, "Hello");
-}
-
-// Mixed named + positional
-
-#[test]
-fn test_string_named_and_positional() {
- let (name, file): (String, String) = vec!["--name", "Alice", "file.txt"]
- .to_picker()
- .pick(&arg![name: String])
- .or_default()
- .pick(&arg![String])
- .or_default()
- .unwrap();
- assert_eq!(name, "Alice");
- assert_eq!(file, "file.txt");
-}
-
-// After `--` (end-of-options)
-
-#[test]
-fn test_string_named_after_end_of_options() {
- // Named arg after `--` should not be matched → default
- let val: String = vec!["--", "--name", "Alice"]
- .to_picker()
- .pick(&arg![name: String])
- .or_default()
- .unwrap();
- assert_eq!(val, "");
-}
-
-// Unrelated flag should not match → default
-
-#[test]
-fn test_string_unrelated_flag() {
- let val: String = vec!["--other", "value"]
- .to_picker()
- .pick(&arg![name: String])
- .or_default()
- .unwrap();
- assert_eq!(val, "");
-}
-
-// to_result / to_option
-
-#[test]
-fn test_string_to_result() {
- let result: Result<String, ()> = vec!["--name", "Alice"]
- .to_picker()
- .pick(&arg![name: String])
- .or_default()
- .to_result();
- assert_eq!(result, Ok("Alice".to_string()));
-}
-
-#[test]
-fn test_string_to_option() {
- let opt: Option<String> = vec!["--name", "Alice"]
- .to_picker()
- .pick(&arg![name: String])
- .or_default()
- .to_option();
- assert_eq!(opt, Some("Alice".to_string()));
-}
-
-// Custom default via .or()
-
-#[test]
-fn test_string_custom_default() {
- let val: String = Vec::<&str>::new()
- .to_picker()
- .pick(&arg![name: String])
- .or(|| "default_name".to_string())
- .unwrap();
- assert_eq!(val, "default_name");
-}