aboutsummaryrefslogtreecommitdiff
path: root/mingling_picker/src/parselib
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/src/parselib
parente6136f22cff446b16dbebf3b26a6fdea6dbc0e83 (diff)
refactor: rename `mingling_picker` to `arg_picker`
Diffstat (limited to 'mingling_picker/src/parselib')
-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
7 files changed, 0 insertions, 1029 deletions
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()
-}