clap_builder/parser/matches/
matched_arg.rs

1// Std
2use std::{
3    ffi::{OsStr, OsString},
4    iter::{Cloned, Flatten},
5    slice::Iter,
6};
7
8use crate::builder::ArgPredicate;
9use crate::parser::ValueSource;
10use crate::util::eq_ignore_case;
11use crate::util::AnyValue;
12use crate::util::AnyValueId;
13use crate::INTERNAL_ERROR_MSG;
14
15#[derive(Debug, Clone)]
16pub(crate) struct MatchedArg {
17    source: Option<ValueSource>,
18    indices: Vec<usize>,
19    type_id: Option<AnyValueId>,
20    vals: Vec<Vec<AnyValue>>,
21    raw_vals: Vec<Vec<OsString>>,
22    ignore_case: bool,
23}
24
25impl MatchedArg {
26    pub(crate) fn new_arg(arg: &crate::Arg) -> Self {
27        let ignore_case = arg.is_ignore_case_set();
28        Self {
29            source: None,
30            indices: Vec::new(),
31            type_id: Some(arg.get_value_parser().type_id()),
32            vals: Vec::new(),
33            raw_vals: Vec::new(),
34            ignore_case,
35        }
36    }
37
38    pub(crate) fn new_group() -> Self {
39        let ignore_case = false;
40        Self {
41            source: None,
42            indices: Vec::new(),
43            type_id: None,
44            vals: Vec::new(),
45            raw_vals: Vec::new(),
46            ignore_case,
47        }
48    }
49
50    pub(crate) fn new_external(cmd: &crate::Command) -> Self {
51        let ignore_case = false;
52        Self {
53            source: None,
54            indices: Vec::new(),
55            type_id: Some(
56                cmd.get_external_subcommand_value_parser()
57                    .expect(INTERNAL_ERROR_MSG)
58                    .type_id(),
59            ),
60            vals: Vec::new(),
61            raw_vals: Vec::new(),
62            ignore_case,
63        }
64    }
65
66    pub(crate) fn indices(&self) -> Cloned<Iter<'_, usize>> {
67        self.indices.iter().cloned()
68    }
69
70    pub(crate) fn get_index(&self, index: usize) -> Option<usize> {
71        self.indices.get(index).cloned()
72    }
73
74    pub(crate) fn push_index(&mut self, index: usize) {
75        self.indices.push(index);
76    }
77
78    pub(crate) fn vals(&self) -> Iter<'_, Vec<AnyValue>> {
79        self.vals.iter()
80    }
81
82    pub(crate) fn into_vals(self) -> Vec<Vec<AnyValue>> {
83        self.vals
84    }
85
86    pub(crate) fn vals_flatten(&self) -> Flatten<Iter<'_, Vec<AnyValue>>> {
87        self.vals.iter().flatten()
88    }
89
90    pub(crate) fn into_vals_flatten(self) -> Flatten<std::vec::IntoIter<Vec<AnyValue>>> {
91        self.vals.into_iter().flatten()
92    }
93
94    pub(crate) fn raw_vals(&self) -> Iter<'_, Vec<OsString>> {
95        self.raw_vals.iter()
96    }
97
98    pub(crate) fn raw_vals_flatten(&self) -> Flatten<Iter<'_, Vec<OsString>>> {
99        self.raw_vals.iter().flatten()
100    }
101
102    pub(crate) fn first(&self) -> Option<&AnyValue> {
103        self.vals_flatten().next()
104    }
105
106    #[cfg(test)]
107    pub(crate) fn first_raw(&self) -> Option<&OsString> {
108        self.raw_vals_flatten().next()
109    }
110
111    pub(crate) fn new_val_group(&mut self) {
112        self.vals.push(vec![]);
113        self.raw_vals.push(vec![]);
114    }
115
116    pub(crate) fn append_val(&mut self, val: AnyValue, raw_val: OsString) {
117        // We assume there is always a group created before.
118        self.vals.last_mut().expect(INTERNAL_ERROR_MSG).push(val);
119        self.raw_vals
120            .last_mut()
121            .expect(INTERNAL_ERROR_MSG)
122            .push(raw_val);
123    }
124
125    pub(crate) fn num_vals(&self) -> usize {
126        self.vals.iter().map(|v| v.len()).sum()
127    }
128
129    // Will be used later
130    #[allow(dead_code)]
131    pub(crate) fn num_vals_last_group(&self) -> usize {
132        self.vals.last().map(|x| x.len()).unwrap_or(0)
133    }
134
135    pub(crate) fn all_val_groups_empty(&self) -> bool {
136        self.vals.iter().flatten().count() == 0
137    }
138
139    pub(crate) fn check_explicit(&self, predicate: &ArgPredicate) -> bool {
140        if self.source.map(|s| !s.is_explicit()).unwrap_or(false) {
141            return false;
142        }
143
144        match predicate {
145            ArgPredicate::Equals(val) => self.raw_vals_flatten().any(|v| {
146                if self.ignore_case {
147                    // If `v` isn't utf8, it can't match `val`, so `OsStr::to_str` should be fine
148                    eq_ignore_case(&v.to_string_lossy(), &val.to_string_lossy())
149                } else {
150                    OsString::as_os_str(v) == OsStr::new(val)
151                }
152            }),
153            ArgPredicate::IsPresent => true,
154        }
155    }
156
157    pub(crate) fn source(&self) -> Option<ValueSource> {
158        self.source
159    }
160
161    pub(crate) fn set_source(&mut self, source: ValueSource) {
162        if let Some(existing) = self.source {
163            self.source = Some(existing.max(source));
164        } else {
165            self.source = Some(source);
166        }
167    }
168
169    pub(crate) fn type_id(&self) -> Option<AnyValueId> {
170        self.type_id
171    }
172
173    pub(crate) fn infer_type_id(&self, expected: AnyValueId) -> AnyValueId {
174        self.type_id()
175            .or_else(|| {
176                self.vals_flatten()
177                    .map(|v| v.type_id())
178                    .find(|actual| *actual != expected)
179            })
180            .unwrap_or(expected)
181    }
182}
183
184impl PartialEq for MatchedArg {
185    fn eq(&self, other: &MatchedArg) -> bool {
186        let MatchedArg {
187            source: self_source,
188            indices: self_indices,
189            type_id: self_type_id,
190            vals: _,
191            raw_vals: self_raw_vals,
192            ignore_case: self_ignore_case,
193        } = self;
194        let MatchedArg {
195            source: other_source,
196            indices: other_indices,
197            type_id: other_type_id,
198            vals: _,
199            raw_vals: other_raw_vals,
200            ignore_case: other_ignore_case,
201        } = other;
202        self_source == other_source
203            && self_indices == other_indices
204            && self_type_id == other_type_id
205            && self_raw_vals == other_raw_vals
206            && self_ignore_case == other_ignore_case
207    }
208}
209
210impl Eq for MatchedArg {}
211
212#[cfg(test)]
213mod tests {
214    use super::*;
215
216    #[test]
217    fn test_grouped_vals_first() {
218        let mut m = MatchedArg::new_group();
219        m.new_val_group();
220        m.new_val_group();
221        m.append_val(AnyValue::new(String::from("bbb")), "bbb".into());
222        m.append_val(AnyValue::new(String::from("ccc")), "ccc".into());
223        assert_eq!(m.first_raw(), Some(&OsString::from("bbb")));
224    }
225}