]>
Commit | Line | Data |
---|---|---|
8bb4bdeb | 1 | // Std |
a2a8927a XL |
2 | use std::{ |
3 | collections::{ | |
4 | hash_map::{Entry, Iter}, | |
5 | HashMap, | |
6 | }, | |
7 | ffi::OsStr, | |
8 | mem, | |
9 | ops::Deref, | |
10 | }; | |
8bb4bdeb | 11 | |
8bb4bdeb | 12 | // Internal |
a2a8927a | 13 | use crate::args::{settings::ArgSettings, AnyArg, ArgMatches, MatchedArg, SubCommand}; |
8bb4bdeb XL |
14 | |
15 | #[doc(hidden)] | |
16 | #[allow(missing_debug_implementations)] | |
17 | pub struct ArgMatcher<'a>(pub ArgMatches<'a>); | |
18 | ||
19 | impl<'a> Default for ArgMatcher<'a> { | |
3dfed10e XL |
20 | fn default() -> Self { |
21 | ArgMatcher(ArgMatches::default()) | |
22 | } | |
8bb4bdeb XL |
23 | } |
24 | ||
25 | impl<'a> ArgMatcher<'a> { | |
3dfed10e XL |
26 | pub fn new() -> Self { |
27 | ArgMatcher::default() | |
28 | } | |
8bb4bdeb | 29 | |
3dfed10e XL |
30 | pub fn process_arg_overrides<'b>( |
31 | &mut self, | |
32 | a: Option<&AnyArg<'a, 'b>>, | |
33 | overrides: &mut Vec<(&'b str, &'a str)>, | |
34 | required: &mut Vec<&'a str>, | |
35 | check_all: bool, | |
36 | ) { | |
37 | debugln!( | |
38 | "ArgMatcher::process_arg_overrides:{:?};", | |
39 | a.map_or(None, |a| Some(a.name())) | |
40 | ); | |
0531ce1d XL |
41 | if let Some(aa) = a { |
42 | let mut self_done = false; | |
43 | if let Some(a_overrides) = aa.overrides() { | |
44 | for overr in a_overrides { | |
45 | debugln!("ArgMatcher::process_arg_overrides:iter:{};", overr); | |
46 | if overr == &aa.name() { | |
47 | self_done = true; | |
48 | self.handle_self_overrides(a); | |
49 | } else if self.is_present(overr) { | |
3dfed10e XL |
50 | debugln!( |
51 | "ArgMatcher::process_arg_overrides:iter:{}: removing from matches;", | |
52 | overr | |
53 | ); | |
0531ce1d | 54 | self.remove(overr); |
3dfed10e | 55 | for i in (0..required.len()).rev() { |
0531ce1d | 56 | if &required[i] == overr { |
3dfed10e XL |
57 | debugln!( |
58 | "ArgMatcher::process_arg_overrides:iter:{}: removing required;", | |
59 | overr | |
60 | ); | |
0531ce1d XL |
61 | required.swap_remove(i); |
62 | break; | |
63 | } | |
64 | } | |
65 | overrides.push((overr, aa.name())); | |
66 | } else { | |
67 | overrides.push((overr, aa.name())); | |
68 | } | |
69 | } | |
70 | } | |
71 | if check_all && !self_done { | |
72 | self.handle_self_overrides(a); | |
73 | } | |
74 | } | |
75 | } | |
76 | ||
77 | pub fn handle_self_overrides<'b>(&mut self, a: Option<&AnyArg<'a, 'b>>) { | |
3dfed10e XL |
78 | debugln!( |
79 | "ArgMatcher::handle_self_overrides:{:?};", | |
80 | a.map_or(None, |a| Some(a.name())) | |
81 | ); | |
0531ce1d XL |
82 | if let Some(aa) = a { |
83 | if !aa.has_switch() || aa.is_set(ArgSettings::Multiple) { | |
84 | // positional args can't override self or else we would never advance to the next | |
85 | ||
86 | // Also flags with --multiple set are ignored otherwise we could never have more | |
87 | // than one | |
88 | return; | |
89 | } | |
90 | if let Some(ma) = self.get_mut(aa.name()) { | |
91 | if ma.vals.len() > 1 { | |
92 | // swap_remove(0) would be O(1) but does not preserve order, which | |
93 | // we need | |
94 | ma.vals.remove(0); | |
95 | ma.occurs = 1; | |
96 | } else if !aa.takes_value() && ma.occurs > 1 { | |
97 | ma.occurs = 1; | |
98 | } | |
99 | } | |
100 | } | |
101 | } | |
102 | ||
103 | pub fn is_present(&self, name: &str) -> bool { | |
104 | self.0.is_present(name) | |
105 | } | |
106 | ||
ff7c6d11 | 107 | pub fn propagate_globals(&mut self, global_arg_vec: &[&'a str]) { |
3dfed10e XL |
108 | debugln!( |
109 | "ArgMatcher::get_global_values: global_arg_vec={:?}", | |
110 | global_arg_vec | |
111 | ); | |
abe05a73 XL |
112 | let mut vals_map = HashMap::new(); |
113 | self.fill_in_global_values(global_arg_vec, &mut vals_map); | |
114 | } | |
115 | ||
116 | fn fill_in_global_values( | |
ff7c6d11 XL |
117 | &mut self, |
118 | global_arg_vec: &[&'a str], | |
119 | vals_map: &mut HashMap<&'a str, MatchedArg>, | |
abe05a73 XL |
120 | ) { |
121 | for global_arg in global_arg_vec { | |
122 | if let Some(ma) = self.get(global_arg) { | |
123 | // We have to check if the parent's global arg wasn't used but still exists | |
124 | // such as from a default value. | |
125 | // | |
126 | // For example, `myprog subcommand --global-arg=value` where --global-arg defines | |
127 | // a default value of `other` myprog would have an existing MatchedArg for | |
128 | // --global-arg where the value is `other`, however the occurs will be 0. | |
129 | let to_update = if let Some(parent_ma) = vals_map.get(global_arg) { | |
130 | if parent_ma.occurs > 0 && ma.occurs == 0 { | |
131 | parent_ma.clone() | |
132 | } else { | |
133 | ma.clone() | |
134 | } | |
135 | } else { | |
136 | ma.clone() | |
137 | }; | |
138 | vals_map.insert(global_arg, to_update); | |
041b39d2 | 139 | } |
abe05a73 XL |
140 | } |
141 | if let Some(ref mut sc) = self.0.subcommand { | |
041b39d2 | 142 | let mut am = ArgMatcher(mem::replace(&mut sc.matches, ArgMatches::new())); |
abe05a73 | 143 | am.fill_in_global_values(global_arg_vec, vals_map); |
041b39d2 | 144 | mem::swap(&mut am.0, &mut sc.matches); |
abe05a73 XL |
145 | } |
146 | ||
a2a8927a | 147 | for (name, matched_arg) in vals_map.iter_mut() { |
abe05a73 | 148 | self.0.args.insert(name, matched_arg.clone()); |
041b39d2 XL |
149 | } |
150 | } | |
151 | ||
3dfed10e XL |
152 | pub fn get_mut(&mut self, arg: &str) -> Option<&mut MatchedArg> { |
153 | self.0.args.get_mut(arg) | |
154 | } | |
8bb4bdeb | 155 | |
3dfed10e XL |
156 | pub fn get(&self, arg: &str) -> Option<&MatchedArg> { |
157 | self.0.args.get(arg) | |
158 | } | |
8bb4bdeb | 159 | |
3dfed10e XL |
160 | pub fn remove(&mut self, arg: &str) { |
161 | self.0.args.remove(arg); | |
162 | } | |
8bb4bdeb XL |
163 | |
164 | pub fn remove_all(&mut self, args: &[&str]) { | |
165 | for &arg in args { | |
166 | self.0.args.remove(arg); | |
167 | } | |
168 | } | |
169 | ||
3dfed10e XL |
170 | pub fn insert(&mut self, name: &'a str) { |
171 | self.0.args.insert(name, MatchedArg::new()); | |
172 | } | |
8bb4bdeb | 173 | |
3dfed10e XL |
174 | pub fn contains(&self, arg: &str) -> bool { |
175 | self.0.args.contains_key(arg) | |
176 | } | |
8bb4bdeb | 177 | |
3dfed10e XL |
178 | pub fn is_empty(&self) -> bool { |
179 | self.0.args.is_empty() | |
180 | } | |
8bb4bdeb | 181 | |
3dfed10e XL |
182 | pub fn usage(&mut self, usage: String) { |
183 | self.0.usage = Some(usage); | |
184 | } | |
8bb4bdeb | 185 | |
3dfed10e XL |
186 | pub fn arg_names(&'a self) -> Vec<&'a str> { |
187 | self.0.args.keys().map(Deref::deref).collect() | |
188 | } | |
8bb4bdeb | 189 | |
3dfed10e XL |
190 | pub fn entry(&mut self, arg: &'a str) -> Entry<&'a str, MatchedArg> { |
191 | self.0.args.entry(arg) | |
192 | } | |
8bb4bdeb | 193 | |
3dfed10e XL |
194 | pub fn subcommand(&mut self, sc: SubCommand<'a>) { |
195 | self.0.subcommand = Some(Box::new(sc)); | |
196 | } | |
8bb4bdeb | 197 | |
3dfed10e XL |
198 | pub fn subcommand_name(&self) -> Option<&str> { |
199 | self.0.subcommand_name() | |
200 | } | |
8bb4bdeb | 201 | |
3dfed10e XL |
202 | pub fn iter(&self) -> Iter<&str, MatchedArg> { |
203 | self.0.args.iter() | |
204 | } | |
8bb4bdeb XL |
205 | |
206 | pub fn inc_occurrence_of(&mut self, arg: &'a str) { | |
041b39d2 | 207 | debugln!("ArgMatcher::inc_occurrence_of: arg={}", arg); |
8bb4bdeb XL |
208 | if let Some(a) = self.get_mut(arg) { |
209 | a.occurs += 1; | |
210 | return; | |
211 | } | |
041b39d2 | 212 | debugln!("ArgMatcher::inc_occurrence_of: first instance"); |
8bb4bdeb XL |
213 | self.insert(arg); |
214 | } | |
215 | ||
216 | pub fn inc_occurrences_of(&mut self, args: &[&'a str]) { | |
041b39d2 | 217 | debugln!("ArgMatcher::inc_occurrences_of: args={:?}", args); |
8bb4bdeb XL |
218 | for arg in args { |
219 | self.inc_occurrence_of(arg); | |
220 | } | |
221 | } | |
222 | ||
223 | pub fn add_val_to(&mut self, arg: &'a str, val: &OsStr) { | |
224 | let ma = self.entry(arg).or_insert(MatchedArg { | |
225 | occurs: 0, | |
0531ce1d | 226 | indices: Vec::with_capacity(1), |
041b39d2 | 227 | vals: Vec::with_capacity(1), |
8bb4bdeb | 228 | }); |
041b39d2 | 229 | ma.vals.push(val.to_owned()); |
8bb4bdeb XL |
230 | } |
231 | ||
0531ce1d XL |
232 | pub fn add_index_to(&mut self, arg: &'a str, idx: usize) { |
233 | let ma = self.entry(arg).or_insert(MatchedArg { | |
234 | occurs: 0, | |
235 | indices: Vec::with_capacity(1), | |
236 | vals: Vec::new(), | |
237 | }); | |
238 | ma.indices.push(idx); | |
239 | } | |
240 | ||
8bb4bdeb | 241 | pub fn needs_more_vals<'b, A>(&self, o: &A) -> bool |
ff7c6d11 XL |
242 | where |
243 | A: AnyArg<'a, 'b>, | |
8bb4bdeb | 244 | { |
041b39d2 | 245 | debugln!("ArgMatcher::needs_more_vals: o={}", o.name()); |
8bb4bdeb XL |
246 | if let Some(ma) = self.get(o.name()) { |
247 | if let Some(num) = o.num_vals() { | |
041b39d2 | 248 | debugln!("ArgMatcher::needs_more_vals: num_vals...{}", num); |
8bb4bdeb XL |
249 | return if o.is_set(ArgSettings::Multiple) { |
250 | ((ma.vals.len() as u64) % num) != 0 | |
251 | } else { | |
252 | num != (ma.vals.len() as u64) | |
253 | }; | |
254 | } else if let Some(num) = o.max_vals() { | |
041b39d2 | 255 | debugln!("ArgMatcher::needs_more_vals: max_vals...{}", num); |
a2a8927a | 256 | return (ma.vals.len() as u64) <= num; |
8bb4bdeb | 257 | } else if o.min_vals().is_some() { |
041b39d2 | 258 | debugln!("ArgMatcher::needs_more_vals: min_vals...true"); |
8bb4bdeb XL |
259 | return true; |
260 | } | |
261 | return o.is_set(ArgSettings::Multiple); | |
262 | } | |
263 | true | |
264 | } | |
265 | } | |
266 | ||
a2a8927a XL |
267 | // Not changing to From just to not deal with possible breaking changes on v2 since v3 is coming |
268 | // in the future anyways | |
269 | #[cfg_attr(feature = "cargo-clippy", allow(clippy::from_over_into))] | |
8bb4bdeb | 270 | impl<'a> Into<ArgMatches<'a>> for ArgMatcher<'a> { |
3dfed10e XL |
271 | fn into(self) -> ArgMatches<'a> { |
272 | self.0 | |
273 | } | |
8bb4bdeb | 274 | } |