aboutsummaryrefslogtreecommitdiff
path: root/mingling_picker/src/parselib/flag_matcher.rs
blob: e93d35a079b537a6f4a99a1566dc0a969e79e532 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
}