diff options
| author | 魏曹先生 <1992414357@qq.com> | 2026-07-17 11:08:07 +0800 |
|---|---|---|
| committer | 魏曹先生 <1992414357@qq.com> | 2026-07-17 11:11:28 +0800 |
| commit | 79ec6878877f0fd9246d67d3cd4f8cc2d1200150 (patch) | |
| tree | e3cd8cfc7ef5cd2a6a7ceb93d9a4b1764fa21b61 /arg_picker/src/parselib/multi_arg_matcher.rs | |
| parent | e6136f22cff446b16dbebf3b26a6fdea6dbc0e83 (diff) | |
refactor: rename `mingling_picker` to `arg_picker`
Diffstat (limited to 'arg_picker/src/parselib/multi_arg_matcher.rs')
| -rw-r--r-- | arg_picker/src/parselib/multi_arg_matcher.rs | 141 |
1 files changed, 141 insertions, 0 deletions
diff --git a/arg_picker/src/parselib/multi_arg_matcher.rs b/arg_picker/src/parselib/multi_arg_matcher.rs new file mode 100644 index 0000000..748b1be --- /dev/null +++ b/arg_picker/src/parselib/multi_arg_matcher.rs @@ -0,0 +1,141 @@ +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)) + }) + } +} |
