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}
226