]>
Commit | Line | Data |
---|---|---|
8bb4bdeb | 1 | // Std |
8bb4bdeb XL |
2 | use std::ffi::{OsStr, OsString}; |
3 | use std::fmt::Display; | |
4 | use std::fs::File; | |
5 | use std::io::{self, BufWriter, Write}; | |
6 | #[cfg(feature = "debug")] | |
7 | use std::os::unix::ffi::OsStrExt; | |
8 | use std::path::PathBuf; | |
9 | use std::slice::Iter; | |
10 | use std::iter::Peekable; | |
11 | ||
12 | // Third Party | |
13 | use vec_map::{self, VecMap}; | |
14 | ||
15 | // Internal | |
16 | use INTERNAL_ERROR_MSG; | |
17 | use INVALID_UTF8; | |
18 | use SubCommand; | |
19 | use app::App; | |
20 | use app::help::Help; | |
21 | use app::meta::AppMeta; | |
041b39d2 | 22 | use app::settings::AppFlags; |
8bb4bdeb | 23 | use args::{AnyArg, ArgMatcher, Base, Switched, Arg, ArgGroup, FlagBuilder, OptBuilder, PosBuilder}; |
8bb4bdeb XL |
24 | use args::settings::ArgSettings; |
25 | use completions::ComplGen; | |
26 | use errors::{Error, ErrorKind}; | |
27 | use errors::Result as ClapResult; | |
041b39d2 | 28 | use fmt::ColorWhen; |
8bb4bdeb XL |
29 | use osstringext::OsStrExt2; |
30 | use completions::Shell; | |
31 | use suggestions; | |
041b39d2 XL |
32 | use app::settings::AppSettings as AS; |
33 | use app::validator::Validator; | |
34 | use app::usage; | |
35 | ||
36 | #[derive(Debug, PartialEq, Copy, Clone)] | |
37 | #[doc(hidden)] | |
38 | pub enum ParseResult<'a> { | |
39 | Flag, | |
40 | Opt(&'a str), | |
41 | Pos(&'a str), | |
42 | MaybeHyphenValue, | |
43 | MaybeNegNum, | |
44 | NotFound, | |
45 | ValuesDone, | |
46 | } | |
8bb4bdeb XL |
47 | |
48 | #[allow(missing_debug_implementations)] | |
49 | #[doc(hidden)] | |
041b39d2 | 50 | #[derive(Clone, Default)] |
8bb4bdeb XL |
51 | pub struct Parser<'a, 'b> |
52 | where 'a: 'b | |
53 | { | |
041b39d2 XL |
54 | pub meta: AppMeta<'b>, |
55 | settings: AppFlags, | |
56 | pub g_settings: AppFlags, | |
8bb4bdeb | 57 | pub flags: Vec<FlagBuilder<'a, 'b>>, |
8bb4bdeb | 58 | pub opts: Vec<OptBuilder<'a, 'b>>, |
8bb4bdeb | 59 | pub positionals: VecMap<PosBuilder<'a, 'b>>, |
8bb4bdeb | 60 | pub subcommands: Vec<App<'a, 'b>>, |
041b39d2 XL |
61 | pub groups: Vec<ArgGroup<'a>>, |
62 | pub global_args: Vec<Arg<'a, 'b>>, | |
63 | pub required: Vec<&'a str>, | |
64 | pub r_ifs: Vec<(&'a str, &'b str, &'a str)>, | |
65 | pub blacklist: Vec<&'b str>, | |
66 | pub overrides: Vec<&'b str>, | |
8bb4bdeb XL |
67 | help_short: Option<char>, |
68 | version_short: Option<char>, | |
041b39d2 XL |
69 | cache: Option<&'a str>, |
70 | pub help_message: Option<&'a str>, | |
71 | pub version_message: Option<&'a str>, | |
8bb4bdeb XL |
72 | } |
73 | ||
74 | impl<'a, 'b> Parser<'a, 'b> | |
75 | where 'a: 'b | |
76 | { | |
77 | pub fn with_name(n: String) -> Self { | |
041b39d2 XL |
78 | Parser { |
79 | meta: AppMeta::with_name(n), | |
80 | ..Default::default() | |
81 | } | |
8bb4bdeb XL |
82 | } |
83 | ||
84 | pub fn help_short(&mut self, s: &str) { | |
041b39d2 | 85 | let c = s.trim_left_matches(|c| c == '-') |
8bb4bdeb | 86 | .chars() |
041b39d2 XL |
87 | .nth(0) |
88 | .unwrap_or('h'); | |
89 | self.help_short = Some(c); | |
8bb4bdeb XL |
90 | } |
91 | ||
92 | pub fn version_short(&mut self, s: &str) { | |
041b39d2 | 93 | let c = s.trim_left_matches(|c| c == '-') |
8bb4bdeb | 94 | .chars() |
041b39d2 XL |
95 | .nth(0) |
96 | .unwrap_or('V'); | |
97 | self.version_short = Some(c); | |
8bb4bdeb XL |
98 | } |
99 | ||
100 | pub fn gen_completions_to<W: Write>(&mut self, for_shell: Shell, buf: &mut W) { | |
041b39d2 XL |
101 | if !self.is_set(AS::Propogated) { |
102 | self.propogate_help_version(); | |
103 | self.build_bin_names(); | |
104 | self.propogate_globals(); | |
105 | self.propogate_settings(); | |
106 | self.set(AS::Propogated); | |
107 | } | |
8bb4bdeb XL |
108 | |
109 | ComplGen::new(self).generate(for_shell, buf) | |
110 | } | |
111 | ||
112 | pub fn gen_completions(&mut self, for_shell: Shell, od: OsString) { | |
113 | use std::error::Error; | |
114 | ||
115 | let out_dir = PathBuf::from(od); | |
7cac9316 | 116 | let name = &*self.meta.bin_name.as_ref().unwrap().clone(); |
8bb4bdeb XL |
117 | let file_name = match for_shell { |
118 | Shell::Bash => format!("{}.bash-completion", name), | |
119 | Shell::Fish => format!("{}.fish", name), | |
120 | Shell::Zsh => format!("_{}", name), | |
121 | Shell::PowerShell => format!("_{}.ps1", name), | |
122 | }; | |
123 | ||
124 | let mut file = match File::create(out_dir.join(file_name)) { | |
125 | Err(why) => panic!("couldn't create completion file: {}", why.description()), | |
126 | Ok(file) => file, | |
127 | }; | |
128 | self.gen_completions_to(for_shell, &mut file) | |
129 | } | |
130 | ||
041b39d2 XL |
131 | #[inline] |
132 | fn app_debug_asserts(&mut self) -> bool { | |
133 | assert!(self.verify_positionals()); | |
134 | let should_err = self.groups | |
135 | .iter() | |
136 | .all(|g| { | |
137 | g.args | |
138 | .iter() | |
139 | .all(|arg| { | |
140 | (self.flags.iter().any(|f| &f.b.name == arg) || | |
141 | self.opts.iter().any(|o| &o.b.name == arg) || | |
142 | self.positionals.values().any(|p| &p.b.name == arg) || | |
143 | self.groups.iter().any(|g| &g.name == arg)) | |
144 | }) | |
145 | }); | |
146 | let g = self.groups | |
147 | .iter() | |
148 | .find(|g| { | |
149 | g.args | |
150 | .iter() | |
151 | .any(|arg| { | |
152 | !(self.flags.iter().any(|f| &f.b.name == arg) || | |
153 | self.opts.iter().any(|o| &o.b.name == arg) || | |
154 | self.positionals.values().any(|p| &p.b.name == arg) || | |
155 | self.groups.iter().any(|g| &g.name == arg)) | |
156 | }) | |
157 | }); | |
158 | assert!(should_err, | |
159 | "The group '{}' contains the arg '{}' that doesn't actually exist.", | |
160 | g.unwrap().name, | |
161 | g.unwrap() | |
162 | .args | |
163 | .iter() | |
164 | .find(|arg| { | |
165 | !(self.flags.iter().any(|f| &&f.b.name == arg) || | |
166 | self.opts.iter().any(|o| &&o.b.name == arg) || | |
167 | self.positionals.values().any(|p| &&p.b.name == arg) || | |
168 | self.groups.iter().any(|g| &&g.name == arg)) | |
169 | }) | |
170 | .unwrap()); | |
171 | true | |
172 | } | |
173 | ||
174 | #[inline] | |
175 | fn debug_asserts(&self, a: &Arg) -> bool { | |
176 | assert!(!arg_names!(self).any(|name| name == a.b.name), | |
177 | format!("Non-unique argument name: {} is already in use", a.b.name)); | |
178 | if let Some(l) = a.s.long { | |
179 | assert!(!self.contains_long(l), | |
180 | "Argument long must be unique\n\n\t--{} is already in use", | |
181 | l); | |
182 | } | |
183 | if let Some(s) = a.s.short { | |
184 | assert!(!self.contains_short(s), | |
185 | "Argument short must be unique\n\n\t-{} is already in use", | |
186 | s); | |
187 | } | |
188 | let i = if a.index.is_none() { | |
189 | (self.positionals.len() + 1) | |
190 | } else { | |
191 | a.index.unwrap() as usize | |
192 | }; | |
193 | assert!(!self.positionals.contains_key(i), | |
194 | "Argument \"{}\" has the same index as another positional \ | |
195 | argument\n\n\tPerhaps try .multiple(true) to allow one positional argument \ | |
196 | to take multiple values", | |
197 | a.b.name); | |
198 | assert!(!(a.is_set(ArgSettings::Required) && a.is_set(ArgSettings::Global)), | |
199 | "Global arguments cannot be required.\n\n\t'{}' is marked as \ | |
200 | global and required", | |
201 | a.b.name); | |
202 | if a.b.is_set(ArgSettings::Last) { | |
203 | assert!(!self.positionals | |
204 | .values() | |
205 | .any(|p| p.b.is_set(ArgSettings::Last)), | |
206 | "Only one positional argument may have last(true) set. Found two."); | |
207 | assert!(a.s.long.is_none(), | |
208 | "Flags or Options may not have last(true) set. {} has both a long and last(true) set.", | |
209 | a.b.name); | |
210 | assert!(a.s.short.is_none(), | |
211 | "Flags or Options may not have last(true) set. {} has both a short and last(true) set.", | |
212 | a.b.name); | |
213 | } | |
214 | true | |
215 | } | |
216 | ||
217 | #[inline] | |
218 | fn add_conditional_reqs(&mut self, a: &Arg<'a, 'b>) { | |
219 | if let Some(ref r_ifs) = a.r_ifs { | |
220 | for &(arg, val) in r_ifs { | |
221 | self.r_ifs.push((arg, val, a.b.name)); | |
8bb4bdeb XL |
222 | } |
223 | } | |
041b39d2 XL |
224 | } |
225 | ||
226 | #[inline] | |
227 | fn add_arg_groups(&mut self, a: &Arg<'a, 'b>) { | |
228 | if let Some(ref grps) = a.b.groups { | |
229 | for g in grps { | |
230 | let mut found = false; | |
231 | if let Some(ref mut ag) = self.groups.iter_mut().find(|grp| &grp.name == g) { | |
232 | ag.args.push(a.b.name); | |
233 | found = true; | |
234 | } | |
235 | if !found { | |
236 | let mut ag = ArgGroup::with_name(g); | |
237 | ag.args.push(a.b.name); | |
238 | self.groups.push(ag); | |
239 | } | |
8bb4bdeb XL |
240 | } |
241 | } | |
041b39d2 XL |
242 | } |
243 | ||
244 | #[inline] | |
245 | fn add_reqs(&mut self, a: &Arg<'a, 'b>) { | |
7cac9316 | 246 | if a.is_set(ArgSettings::Required) { |
041b39d2 XL |
247 | // If the arg is required, add all it's requirements to master required list |
248 | if let Some(ref areqs) = a.b.requires { | |
249 | for name in areqs | |
250 | .iter() | |
251 | .filter(|&&(val, _)| val.is_none()) | |
252 | .map(|&(_, name)| name) { | |
253 | self.required.push(name); | |
254 | } | |
255 | } | |
256 | self.required.push(a.b.name); | |
257 | } | |
258 | } | |
259 | ||
260 | #[inline] | |
261 | fn implied_settings(&mut self, a: &Arg<'a, 'b>) { | |
262 | if a.is_set(ArgSettings::Last) { | |
263 | // if an arg has `Last` set, we need to imply DontCollapseArgsInUsage so that args | |
264 | // in the usage string don't get confused or left out. | |
265 | self.set(AS::DontCollapseArgsInUsage); | |
266 | self.set(AS::ContainsLast); | |
267 | } | |
268 | if let Some(l) = a.s.long { | |
269 | if l == "version" { | |
270 | self.unset(AS::NeedsLongVersion); | |
271 | } else if l == "help" { | |
272 | self.unset(AS::NeedsLongHelp); | |
273 | } | |
cc61c64b | 274 | } |
041b39d2 XL |
275 | } |
276 | ||
277 | // actually adds the arguments | |
278 | pub fn add_arg(&mut self, a: Arg<'a, 'b>) { | |
279 | // if it's global we have to clone anyways | |
280 | if a.is_set(ArgSettings::Global) { | |
281 | return self.add_arg_ref(&a); | |
282 | } | |
283 | debug_assert!(self.debug_asserts(&a)); | |
284 | self.add_conditional_reqs(&a); | |
285 | self.add_arg_groups(&a); | |
286 | self.add_reqs(&a); | |
287 | self.implied_settings(&a); | |
288 | if a.index.is_some() || (a.s.short.is_none() && a.s.long.is_none()) { | |
cc61c64b XL |
289 | let i = if a.index.is_none() { |
290 | (self.positionals.len() + 1) | |
291 | } else { | |
292 | a.index.unwrap() as usize | |
293 | }; | |
041b39d2 XL |
294 | self.positionals |
295 | .insert(i, PosBuilder::from_arg(a, i as u64)); | |
296 | } else if a.is_set(ArgSettings::TakesValue) { | |
297 | let mut ob = OptBuilder::from(a); | |
298 | ob.s.unified_ord = self.flags.len() + self.opts.len(); | |
299 | self.opts.push(ob); | |
300 | } else { | |
301 | let mut fb = FlagBuilder::from(a); | |
302 | fb.s.unified_ord = self.flags.len() + self.opts.len(); | |
303 | self.flags.push(fb); | |
304 | } | |
305 | } | |
306 | // actually adds the arguments but from a borrow (which means we have to do some clonine) | |
307 | pub fn add_arg_ref(&mut self, a: &Arg<'a, 'b>) { | |
308 | debug_assert!(self.debug_asserts(a)); | |
309 | self.add_conditional_reqs(a); | |
310 | self.add_arg_groups(a); | |
311 | self.add_reqs(a); | |
312 | self.implied_settings(a); | |
313 | if a.index.is_some() || (a.s.short.is_none() && a.s.long.is_none()) { | |
314 | let i = if a.index.is_none() { | |
315 | (self.positionals.len() + 1) | |
316 | } else { | |
317 | a.index.unwrap() as usize | |
318 | }; | |
319 | let pb = PosBuilder::from_arg_ref(a, i as u64); | |
8bb4bdeb XL |
320 | self.positionals.insert(i, pb); |
321 | } else if a.is_set(ArgSettings::TakesValue) { | |
041b39d2 | 322 | let mut ob = OptBuilder::from(a); |
8bb4bdeb | 323 | ob.s.unified_ord = self.flags.len() + self.opts.len(); |
041b39d2 | 324 | self.opts.push(ob); |
8bb4bdeb XL |
325 | } else { |
326 | let mut fb = FlagBuilder::from(a); | |
8bb4bdeb | 327 | fb.s.unified_ord = self.flags.len() + self.opts.len(); |
041b39d2 | 328 | self.flags.push(fb); |
8bb4bdeb XL |
329 | } |
330 | if a.is_set(ArgSettings::Global) { | |
8bb4bdeb XL |
331 | self.global_args.push(a.into()); |
332 | } | |
333 | } | |
334 | ||
335 | pub fn add_group(&mut self, group: ArgGroup<'a>) { | |
336 | if group.required { | |
041b39d2 | 337 | self.required.push(group.name); |
8bb4bdeb XL |
338 | if let Some(ref reqs) = group.requires { |
339 | self.required.extend_from_slice(reqs); | |
340 | } | |
341 | if let Some(ref bl) = group.conflicts { | |
342 | self.blacklist.extend_from_slice(bl); | |
343 | } | |
344 | } | |
041b39d2 XL |
345 | if self.groups.iter().any(|g| g.name == group.name) { |
346 | let grp = self.groups | |
347 | .iter_mut() | |
348 | .find(|g| g.name == group.name) | |
349 | .expect(INTERNAL_ERROR_MSG); | |
8bb4bdeb XL |
350 | grp.args.extend_from_slice(&group.args); |
351 | grp.requires = group.requires.clone(); | |
352 | grp.conflicts = group.conflicts.clone(); | |
353 | grp.required = group.required; | |
041b39d2 XL |
354 | } else { |
355 | self.groups.push(group); | |
8bb4bdeb XL |
356 | } |
357 | } | |
358 | ||
359 | pub fn add_subcommand(&mut self, mut subcmd: App<'a, 'b>) { | |
041b39d2 XL |
360 | debugln!("Parser::add_subcommand: term_w={:?}, name={}", |
361 | self.meta.term_w, | |
362 | subcmd.p.meta.name); | |
8bb4bdeb XL |
363 | subcmd.p.meta.term_w = self.meta.term_w; |
364 | if subcmd.p.meta.name == "help" { | |
041b39d2 | 365 | self.unset(AS::NeedsSubcommandHelp); |
8bb4bdeb XL |
366 | } |
367 | ||
368 | self.subcommands.push(subcmd); | |
369 | } | |
370 | ||
371 | pub fn propogate_settings(&mut self) { | |
041b39d2 XL |
372 | debugln!("Parser::propogate_settings: self={}, g_settings={:#?}", |
373 | self.meta.name, | |
374 | self.g_settings); | |
8bb4bdeb | 375 | for sc in &mut self.subcommands { |
041b39d2 XL |
376 | debugln!("Parser::propogate_settings: sc={}, settings={:#?}, g_settings={:#?}", |
377 | sc.p.meta.name, | |
378 | sc.p.settings, | |
379 | sc.p.g_settings); | |
8bb4bdeb XL |
380 | // We have to create a new scope in order to tell rustc the borrow of `sc` is |
381 | // done and to recursively call this method | |
382 | { | |
041b39d2 XL |
383 | let vsc = self.settings.is_set(AS::VersionlessSubcommands); |
384 | let gv = self.settings.is_set(AS::GlobalVersion); | |
8bb4bdeb XL |
385 | |
386 | if vsc { | |
041b39d2 | 387 | sc.p.set(AS::DisableVersion); |
8bb4bdeb | 388 | } |
7cac9316 | 389 | if gv && sc.p.meta.version.is_none() && self.meta.version.is_some() { |
041b39d2 | 390 | sc.p.set(AS::GlobalVersion); |
8bb4bdeb XL |
391 | sc.p.meta.version = Some(self.meta.version.unwrap()); |
392 | } | |
041b39d2 XL |
393 | sc.p.settings = sc.p.settings | self.g_settings; |
394 | sc.p.g_settings = sc.p.g_settings | self.g_settings; | |
395 | sc.p.meta.term_w = self.meta.term_w; | |
396 | sc.p.meta.max_w = self.meta.max_w; | |
8bb4bdeb XL |
397 | } |
398 | sc.p.propogate_settings(); | |
399 | } | |
400 | } | |
401 | ||
402 | #[cfg_attr(feature = "lints", allow(needless_borrow))] | |
403 | pub fn derive_display_order(&mut self) { | |
041b39d2 XL |
404 | if self.is_set(AS::DeriveDisplayOrder) { |
405 | let unified = self.is_set(AS::UnifiedHelpMessage); | |
8bb4bdeb | 406 | for (i, o) in self.opts |
041b39d2 XL |
407 | .iter_mut() |
408 | .enumerate() | |
409 | .filter(|&(_, ref o)| o.s.disp_ord == 999) { | |
8bb4bdeb XL |
410 | o.s.disp_ord = if unified { o.s.unified_ord } else { i }; |
411 | } | |
412 | for (i, f) in self.flags | |
041b39d2 XL |
413 | .iter_mut() |
414 | .enumerate() | |
415 | .filter(|&(_, ref f)| f.s.disp_ord == 999) { | |
8bb4bdeb XL |
416 | f.s.disp_ord = if unified { f.s.unified_ord } else { i }; |
417 | } | |
418 | for (i, sc) in &mut self.subcommands | |
041b39d2 XL |
419 | .iter_mut() |
420 | .enumerate() | |
421 | .filter(|&(_, ref sc)| sc.p.meta.disp_ord == 999) { | |
8bb4bdeb XL |
422 | sc.p.meta.disp_ord = i; |
423 | } | |
424 | } | |
425 | for sc in &mut self.subcommands { | |
426 | sc.p.derive_display_order(); | |
427 | } | |
428 | } | |
429 | ||
430 | pub fn required(&self) -> Iter<&str> { self.required.iter() } | |
431 | ||
041b39d2 XL |
432 | #[cfg_attr(feature = "lints", allow(needless_borrow))] |
433 | #[inline] | |
434 | pub fn has_args(&self) -> bool { | |
435 | !(self.flags.is_empty() && self.opts.is_empty() && self.positionals.is_empty()) | |
8bb4bdeb XL |
436 | } |
437 | ||
438 | #[inline] | |
439 | pub fn has_opts(&self) -> bool { !self.opts.is_empty() } | |
440 | ||
441 | #[inline] | |
442 | pub fn has_flags(&self) -> bool { !self.flags.is_empty() } | |
443 | ||
444 | #[inline] | |
445 | pub fn has_positionals(&self) -> bool { !self.positionals.is_empty() } | |
446 | ||
447 | #[inline] | |
448 | pub fn has_subcommands(&self) -> bool { !self.subcommands.is_empty() } | |
449 | ||
450 | #[inline] | |
041b39d2 XL |
451 | pub fn has_visible_opts(&self) -> bool { |
452 | if self.opts.is_empty() { | |
453 | return false; | |
454 | } | |
455 | self.opts.iter().any(|o| !o.is_set(ArgSettings::Hidden)) | |
456 | } | |
457 | ||
458 | #[inline] | |
459 | pub fn has_visible_flags(&self) -> bool { | |
460 | if self.flags.is_empty() { | |
461 | return false; | |
462 | } | |
463 | self.flags.iter().any(|f| !f.is_set(ArgSettings::Hidden)) | |
464 | } | |
465 | ||
466 | #[inline] | |
467 | pub fn has_visible_positionals(&self) -> bool { | |
468 | if self.positionals.is_empty() { | |
469 | return false; | |
470 | } | |
471 | self.positionals | |
472 | .values() | |
473 | .any(|p| !p.is_set(ArgSettings::Hidden)) | |
474 | } | |
475 | ||
476 | #[inline] | |
477 | pub fn has_visible_subcommands(&self) -> bool { | |
ea8adc8c | 478 | self.has_subcommands() && self.subcommands.iter().filter(|sc| sc.p.meta.name != "help").any(|sc| !sc.p.is_set(AS::Hidden)) |
041b39d2 XL |
479 | } |
480 | ||
481 | #[inline] | |
482 | pub fn is_set(&self, s: AS) -> bool { self.settings.is_set(s) } | |
cc61c64b XL |
483 | |
484 | #[inline] | |
041b39d2 | 485 | pub fn set(&mut self, s: AS) { self.settings.set(s) } |
cc61c64b XL |
486 | |
487 | #[inline] | |
041b39d2 | 488 | pub fn unset(&mut self, s: AS) { self.settings.unset(s) } |
8bb4bdeb XL |
489 | |
490 | #[cfg_attr(feature = "lints", allow(block_in_if_condition_stmt))] | |
041b39d2 | 491 | pub fn verify_positionals(&mut self) -> bool { |
8bb4bdeb XL |
492 | // Because you must wait until all arguments have been supplied, this is the first chance |
493 | // to make assertions on positional argument indexes | |
494 | // | |
495 | // Firt we verify that the index highest supplied index, is equal to the number of | |
496 | // positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3 | |
497 | // but no 2) | |
7cac9316 | 498 | if let Some((idx, p)) = self.positionals.iter().rev().next() { |
041b39d2 XL |
499 | assert!(!(idx != self.positionals.len()), |
500 | "Found positional argument \"{}\" whose index is {} but there \ | |
501 | are only {} positional arguments defined", | |
502 | p.b.name, | |
503 | idx, | |
504 | self.positionals.len()); | |
8bb4bdeb XL |
505 | } |
506 | ||
507 | // Next we verify that only the highest index has a .multiple(true) (if any) | |
7cac9316 | 508 | if self.positionals |
041b39d2 XL |
509 | .values() |
510 | .any(|a| { | |
511 | a.b.is_set(ArgSettings::Multiple) && | |
512 | (a.index as usize != self.positionals.len()) | |
513 | }) { | |
514 | let mut it = self.positionals.values().rev(); | |
515 | let last = it.next().unwrap(); | |
516 | let second_to_last = it.next().unwrap(); | |
517 | // Either the final positional is required | |
518 | // Or the second to last has a terminator or .last(true) set | |
519 | let ok = last.is_set(ArgSettings::Required) || | |
520 | (second_to_last.v.terminator.is_some() || | |
521 | second_to_last.b.is_set(ArgSettings::Last)) || | |
522 | last.is_set(ArgSettings::Last); | |
523 | assert!(ok, | |
524 | "When using a positional argument with .multiple(true) that is *not the \ | |
525 | last* positional argument, the last positional argument (i.e the one \ | |
526 | with the highest index) *must* have .required(true) or .last(true) set."); | |
527 | let ok = second_to_last.is_set(ArgSettings::Multiple) || last.is_set(ArgSettings::Last); | |
528 | assert!(ok, | |
529 | "Only the last positional argument, or second to last positional \ | |
530 | argument may be set to .multiple(true)"); | |
531 | ||
532 | let count = self.positionals | |
533 | .values() | |
534 | .filter(|p| p.b.settings.is_set(ArgSettings::Multiple) && p.v.num_vals.is_none()) | |
535 | .count(); | |
536 | let ok = count <= 1 || | |
537 | (last.is_set(ArgSettings::Last) && last.is_set(ArgSettings::Multiple) && | |
538 | second_to_last.is_set(ArgSettings::Multiple) && | |
539 | count == 2); | |
540 | assert!(ok, | |
541 | "Only one positional argument with .multiple(true) set is allowed per \ | |
542 | command, unless the second one also has .last(true) set"); | |
543 | } | |
544 | ||
545 | ||
546 | if self.is_set(AS::AllowMissingPositional) { | |
547 | // Check that if a required positional argument is found, all positions with a lower | |
548 | // index are also required. | |
549 | let mut found = false; | |
550 | let mut foundx2 = false; | |
551 | for p in self.positionals.values().rev() { | |
552 | if foundx2 && !p.b.settings.is_set(ArgSettings::Required) { | |
553 | assert!(p.b.is_set(ArgSettings::Required), | |
554 | "Found positional argument which is not required with a lower \ | |
555 | index than a required positional argument by two or more: {:?} \ | |
556 | index {}", | |
557 | p.b.name, | |
558 | p.index); | |
559 | } else if p.b.is_set(ArgSettings::Required) && !p.b.is_set(ArgSettings::Last) { | |
560 | // Args that .last(true) don't count since they can be required and have | |
561 | // positionals with a lower index that aren't required | |
562 | // Imagine: prog <req1> [opt1] -- <req2> | |
563 | // Both of these are valid invocations: | |
564 | // $ prog r1 -- r2 | |
565 | // $ prog r1 o1 -- r2 | |
566 | if found { | |
567 | foundx2 = true; | |
568 | continue; | |
569 | } | |
570 | found = true; | |
571 | continue; | |
572 | } else { | |
573 | found = false; | |
574 | } | |
575 | } | |
576 | } else { | |
577 | // Check that if a required positional argument is found, all positions with a lower | |
578 | // index are also required | |
579 | let mut found = false; | |
580 | for p in self.positionals.values().rev() { | |
581 | if found { | |
582 | assert!(p.b.is_set(ArgSettings::Required), | |
583 | "Found positional argument which is not required with a lower \ | |
584 | index than a required positional argument: {:?} index {}", | |
585 | p.b.name, | |
586 | p.index); | |
587 | } else if p.b.is_set(ArgSettings::Required) && !p.b.is_set(ArgSettings::Last) { | |
588 | // Args that .last(true) don't count since they can be required and have | |
589 | // positionals with a lower index that aren't required | |
590 | // Imagine: prog <req1> [opt1] -- <req2> | |
591 | // Both of these are valid invocations: | |
592 | // $ prog r1 -- r2 | |
593 | // $ prog r1 o1 -- r2 | |
594 | found = true; | |
595 | continue; | |
596 | } | |
8bb4bdeb XL |
597 | } |
598 | } | |
041b39d2 XL |
599 | if self.positionals |
600 | .values() | |
601 | .any(|p| { | |
602 | p.b.is_set(ArgSettings::Last) && p.b.is_set(ArgSettings::Required) | |
603 | }) && self.has_subcommands() && | |
604 | !self.is_set(AS::SubcommandsNegateReqs) { | |
605 | panic!("Having a required positional argument with .last(true) set *and* child \ | |
606 | subcommands without setting SubcommandsNegateReqs isn't compatible."); | |
607 | } | |
608 | ||
609 | true | |
8bb4bdeb XL |
610 | } |
611 | ||
612 | pub fn propogate_globals(&mut self) { | |
613 | for sc in &mut self.subcommands { | |
614 | // We have to create a new scope in order to tell rustc the borrow of `sc` is | |
615 | // done and to recursively call this method | |
616 | { | |
617 | for a in &self.global_args { | |
041b39d2 | 618 | sc.p.add_arg_ref(a); |
8bb4bdeb XL |
619 | } |
620 | } | |
621 | sc.p.propogate_globals(); | |
622 | } | |
623 | } | |
624 | ||
625 | // Checks if the arg matches a subcommand name, or any of it's aliases (if defined) | |
041b39d2 XL |
626 | fn possible_subcommand(&self, arg_os: &OsStr) -> (bool, Option<&str>) { |
627 | debugln!("Parser::possible_subcommand: arg={:?}", arg_os); | |
628 | fn starts(h: &str, n: &OsStr) -> bool { | |
629 | #[cfg(not(target_os = "windows"))] | |
630 | use std::os::unix::ffi::OsStrExt; | |
631 | #[cfg(target_os = "windows")] | |
632 | use osstringext::OsStrExt3; | |
633 | ||
634 | let n_bytes = n.as_bytes(); | |
635 | let h_bytes = OsStr::new(h).as_bytes(); | |
636 | ||
637 | h_bytes.starts_with(n_bytes) | |
638 | } | |
639 | ||
640 | if self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound) { | |
641 | return (false, None); | |
642 | } | |
643 | if !self.is_set(AS::InferSubcommands) { | |
644 | if let Some(sc) = find_subcmd!(self, arg_os) { | |
645 | return (true, Some(&sc.p.meta.name)); | |
646 | } | |
647 | } else { | |
648 | let v = self.subcommands | |
649 | .iter() | |
650 | .filter(|s| { | |
651 | starts(&s.p.meta.name[..], &*arg_os) || | |
652 | (s.p.meta.aliases.is_some() && | |
653 | s.p | |
654 | .meta | |
655 | .aliases | |
656 | .as_ref() | |
657 | .unwrap() | |
658 | .iter() | |
659 | .filter(|&&(a, _)| starts(a, &*arg_os)) | |
660 | .count() == 1) | |
661 | }) | |
662 | .map(|sc| &sc.p.meta.name) | |
663 | .collect::<Vec<_>>(); | |
664 | ||
665 | if v.len() == 1 { | |
666 | return (true, Some(v[0])); | |
667 | } | |
668 | } | |
669 | (false, None) | |
8bb4bdeb XL |
670 | } |
671 | ||
041b39d2 | 672 | fn parse_help_subcommand<I, T>(&self, it: &mut I) -> ClapResult<ParseResult<'a>> |
8bb4bdeb XL |
673 | where I: Iterator<Item = T>, |
674 | T: Into<OsString> | |
675 | { | |
041b39d2 | 676 | debugln!("Parser::parse_help_subcommand;"); |
8bb4bdeb XL |
677 | let cmds: Vec<OsString> = it.map(|c| c.into()).collect(); |
678 | let mut help_help = false; | |
679 | let mut bin_name = self.meta | |
680 | .bin_name | |
681 | .as_ref() | |
682 | .unwrap_or(&self.meta.name) | |
683 | .clone(); | |
684 | let mut sc = { | |
685 | let mut sc: &Parser = self; | |
686 | for (i, cmd) in cmds.iter().enumerate() { | |
687 | if &*cmd.to_string_lossy() == "help" { | |
688 | // cmd help help | |
689 | help_help = true; | |
690 | } | |
691 | if let Some(c) = sc.subcommands | |
041b39d2 XL |
692 | .iter() |
693 | .find(|s| &*s.p.meta.name == cmd) | |
694 | .map(|sc| &sc.p) { | |
8bb4bdeb XL |
695 | sc = c; |
696 | if i == cmds.len() - 1 { | |
697 | break; | |
698 | } | |
699 | } else if let Some(c) = sc.subcommands | |
041b39d2 XL |
700 | .iter() |
701 | .find(|s| if let Some(ref als) = s.p.meta.aliases { | |
702 | als.iter().any(|&(a, _)| a == &*cmd.to_string_lossy()) | |
703 | } else { | |
704 | false | |
705 | }) | |
706 | .map(|sc| &sc.p) { | |
8bb4bdeb XL |
707 | sc = c; |
708 | if i == cmds.len() - 1 { | |
709 | break; | |
710 | } | |
711 | } else { | |
712 | return Err(Error::unrecognized_subcommand(cmd.to_string_lossy().into_owned(), | |
713 | self.meta | |
714 | .bin_name | |
715 | .as_ref() | |
716 | .unwrap_or(&self.meta.name), | |
717 | self.color())); | |
718 | } | |
041b39d2 | 719 | bin_name = format!("{} {}", bin_name, &*sc.meta.name); |
8bb4bdeb XL |
720 | } |
721 | sc.clone() | |
722 | }; | |
723 | if help_help { | |
724 | let mut pb = PosBuilder::new("subcommand", 1); | |
725 | pb.b.help = Some("The subcommand whose help message to display"); | |
726 | pb.set(ArgSettings::Multiple); | |
727 | sc.positionals.insert(1, pb); | |
041b39d2 | 728 | sc.settings = sc.settings | self.g_settings; |
8bb4bdeb XL |
729 | } else { |
730 | sc.create_help_and_version(); | |
731 | } | |
732 | if sc.meta.bin_name != self.meta.bin_name { | |
733 | sc.meta.bin_name = Some(format!("{} {}", bin_name, sc.meta.name)); | |
734 | } | |
041b39d2 | 735 | Err(sc._help(false)) |
8bb4bdeb XL |
736 | } |
737 | ||
041b39d2 XL |
738 | // allow wrong self convention due to self.valid_neg_num = true and it's a private method |
739 | #[cfg_attr(feature = "lints", allow(wrong_self_convention))] | |
740 | fn is_new_arg(&mut self, arg_os: &OsStr, needs_val_of: ParseResult<'a>) -> bool { | |
741 | debugln!("Parser::is_new_arg: arg={:?}, Needs Val of={:?}", | |
742 | arg_os, | |
743 | needs_val_of); | |
744 | let app_wide_settings = if self.is_set(AS::AllowLeadingHyphen) { | |
8bb4bdeb | 745 | true |
041b39d2 | 746 | } else if self.is_set(AS::AllowNegativeNumbers) { |
8bb4bdeb XL |
747 | let a = arg_os.to_string_lossy(); |
748 | if a.parse::<i64>().is_ok() || a.parse::<f64>().is_ok() { | |
041b39d2 | 749 | self.set(AS::ValidNegNumFound); |
8bb4bdeb XL |
750 | true |
751 | } else { | |
752 | false | |
753 | } | |
754 | } else { | |
755 | false | |
756 | }; | |
041b39d2 XL |
757 | let arg_allows_tac = match needs_val_of { |
758 | ParseResult::Opt(name) => { | |
759 | let o = self.opts | |
760 | .iter() | |
761 | .find(|o| o.b.name == name) | |
762 | .expect(INTERNAL_ERROR_MSG); | |
763 | (o.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings) | |
8bb4bdeb | 764 | } |
041b39d2 XL |
765 | ParseResult::Pos(name) => { |
766 | let p = self.positionals | |
767 | .values() | |
768 | .find(|p| p.b.name == name) | |
769 | .expect(INTERNAL_ERROR_MSG); | |
770 | (p.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings) | |
771 | } | |
772 | _ => false, | |
8bb4bdeb | 773 | }; |
041b39d2 XL |
774 | debugln!("Parser::is_new_arg: Arg::allow_leading_hyphen({:?})", |
775 | arg_allows_tac); | |
8bb4bdeb XL |
776 | |
777 | // Is this a new argument, or values from a previous option? | |
8bb4bdeb | 778 | let mut ret = if arg_os.starts_with(b"--") { |
041b39d2 XL |
779 | debugln!("Parser::is_new_arg: -- found"); |
780 | if arg_os.len_() == 2 && !arg_allows_tac { | |
8bb4bdeb | 781 | return true; // We have to return true so override everything else |
041b39d2 XL |
782 | } else if arg_allows_tac { |
783 | return false; | |
8bb4bdeb XL |
784 | } |
785 | true | |
786 | } else if arg_os.starts_with(b"-") { | |
041b39d2 | 787 | debugln!("Parser::is_new_arg: - found"); |
8bb4bdeb XL |
788 | // a singe '-' by itself is a value and typically means "stdin" on unix systems |
789 | !(arg_os.len_() == 1) | |
790 | } else { | |
041b39d2 | 791 | debugln!("Parser::is_new_arg: probably value"); |
8bb4bdeb XL |
792 | false |
793 | }; | |
794 | ||
041b39d2 | 795 | ret = ret && !arg_allows_tac; |
8bb4bdeb | 796 | |
041b39d2 | 797 | debugln!("Parser::is_new_arg: starts_new_arg={:?}", ret); |
8bb4bdeb XL |
798 | ret |
799 | } | |
800 | ||
801 | // The actual parsing function | |
041b39d2 | 802 | #[cfg_attr(feature = "lints", allow(while_let_on_iterator, collapsible_if))] |
8bb4bdeb XL |
803 | pub fn get_matches_with<I, T>(&mut self, |
804 | matcher: &mut ArgMatcher<'a>, | |
805 | it: &mut Peekable<I>) | |
806 | -> ClapResult<()> | |
807 | where I: Iterator<Item = T>, | |
808 | T: Into<OsString> + Clone | |
809 | { | |
041b39d2 | 810 | debugln!("Parser::get_matches_with;"); |
8bb4bdeb | 811 | // Verify all positional assertions pass |
041b39d2 XL |
812 | debug_assert!(self.app_debug_asserts()); |
813 | if self.positionals | |
814 | .values() | |
815 | .any(|a| { | |
816 | a.b.is_set(ArgSettings::Multiple) && | |
817 | (a.index as usize != self.positionals.len()) | |
818 | }) && | |
819 | self.positionals | |
820 | .values() | |
821 | .last() | |
822 | .map_or(false, |p| !p.is_set(ArgSettings::Last)) { | |
823 | self.settings.set(AS::LowIndexMultiplePositional); | |
824 | } | |
825 | let has_args = self.has_args(); | |
8bb4bdeb XL |
826 | |
827 | // Next we create the `--help` and `--version` arguments and add them if | |
828 | // necessary | |
829 | self.create_help_and_version(); | |
830 | ||
831 | let mut subcmd_name: Option<String> = None; | |
041b39d2 | 832 | let mut needs_val_of: ParseResult<'a> = ParseResult::NotFound; |
8bb4bdeb XL |
833 | let mut pos_counter = 1; |
834 | while let Some(arg) = it.next() { | |
835 | let arg_os = arg.into(); | |
041b39d2 XL |
836 | debugln!("Parser::get_matches_with: Begin parsing '{:?}' ({:?})", |
837 | arg_os, | |
838 | &*arg_os.as_bytes()); | |
8bb4bdeb | 839 | |
041b39d2 | 840 | self.unset(AS::ValidNegNumFound); |
8bb4bdeb | 841 | // Is this a new argument, or values from a previous option? |
041b39d2 XL |
842 | let starts_new_arg = self.is_new_arg(&arg_os, needs_val_of); |
843 | if arg_os.starts_with(b"--") && arg_os.len_() == 2 && starts_new_arg { | |
844 | debugln!("Parser::get_matches_with: setting TrailingVals=true"); | |
845 | self.set(AS::TrailingValues); | |
846 | continue; | |
847 | } | |
8bb4bdeb XL |
848 | |
849 | // Has the user already passed '--'? Meaning only positional args follow | |
041b39d2 | 850 | if !self.is_set(AS::TrailingValues) { |
8bb4bdeb | 851 | // Does the arg match a subcommand name, or any of it's aliases (if defined) |
041b39d2 XL |
852 | { |
853 | let (is_match, sc_name) = self.possible_subcommand(&arg_os); | |
854 | debugln!("Parser::get_matches_with: possible_sc={:?}, sc={:?}", | |
855 | is_match, | |
856 | sc_name); | |
857 | if is_match { | |
858 | let sc_name = sc_name.expect(INTERNAL_ERROR_MSG); | |
859 | if sc_name == "help" && self.is_set(AS::NeedsSubcommandHelp) { | |
3b2f2976 | 860 | self.parse_help_subcommand(it)?; |
8bb4bdeb | 861 | } |
041b39d2 XL |
862 | subcmd_name = Some(sc_name.to_owned()); |
863 | break; | |
8bb4bdeb | 864 | } |
7cac9316 | 865 | } |
7cac9316 | 866 | |
041b39d2 XL |
867 | if !starts_new_arg { |
868 | if let ParseResult::Opt(name) = needs_val_of { | |
869 | // Check to see if parsing a value from a previous arg | |
870 | let arg = self.opts | |
871 | .iter() | |
872 | .find(|o| o.b.name == name) | |
873 | .expect(INTERNAL_ERROR_MSG); | |
874 | // get the OptBuilder so we can check the settings | |
3b2f2976 | 875 | needs_val_of = self.add_val_to_arg(arg, &arg_os, matcher)?; |
041b39d2 XL |
876 | // get the next value from the iterator |
877 | continue; | |
878 | } | |
879 | } else if arg_os.starts_with(b"--") { | |
3b2f2976 | 880 | needs_val_of = self.parse_long_arg(matcher, &arg_os)?; |
041b39d2 XL |
881 | debugln!("Parser:get_matches_with: After parse_long_arg {:?}", |
882 | needs_val_of); | |
883 | match needs_val_of { | |
884 | ParseResult::Flag | | |
885 | ParseResult::Opt(..) | | |
886 | ParseResult::ValuesDone => continue, | |
887 | _ => (), | |
7cac9316 XL |
888 | } |
889 | } else if arg_os.starts_with(b"-") && arg_os.len_() != 1 { | |
890 | // Try to parse short args like normal, if AllowLeadingHyphen or | |
891 | // AllowNegativeNumbers is set, parse_short_arg will *not* throw | |
892 | // an error, and instead return Ok(None) | |
3b2f2976 | 893 | needs_val_of = self.parse_short_arg(matcher, &arg_os)?; |
7cac9316 | 894 | // If it's None, we then check if one of those two AppSettings was set |
041b39d2 XL |
895 | debugln!("Parser:get_matches_with: After parse_short_arg {:?}", |
896 | needs_val_of); | |
897 | match needs_val_of { | |
898 | ParseResult::MaybeNegNum => { | |
7cac9316 XL |
899 | if !(arg_os.to_string_lossy().parse::<i64>().is_ok() || |
900 | arg_os.to_string_lossy().parse::<f64>().is_ok()) { | |
901 | return Err(Error::unknown_argument(&*arg_os.to_string_lossy(), | |
041b39d2 XL |
902 | "", |
903 | &*usage::create_error_usage(self, matcher, None), | |
904 | self.color())); | |
8bb4bdeb | 905 | } |
8bb4bdeb | 906 | } |
041b39d2 XL |
907 | ParseResult::Opt(..) | |
908 | ParseResult::Flag | | |
909 | ParseResult::ValuesDone => continue, | |
910 | _ => (), | |
8bb4bdeb XL |
911 | } |
912 | } | |
913 | ||
041b39d2 XL |
914 | if !(self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound)) && |
915 | !self.is_set(AS::InferSubcommands) { | |
916 | if let Some(cdate) = suggestions::did_you_mean(&*arg_os.to_string_lossy(), | |
917 | sc_names!(self)) { | |
918 | return Err(Error::invalid_subcommand(arg_os | |
919 | .to_string_lossy() | |
920 | .into_owned(), | |
921 | cdate, | |
922 | self.meta | |
923 | .bin_name | |
924 | .as_ref() | |
925 | .unwrap_or(&self.meta.name), | |
926 | &*usage::create_error_usage(self, | |
927 | matcher, | |
928 | None), | |
929 | self.color())); | |
8bb4bdeb XL |
930 | } |
931 | } | |
932 | } | |
933 | ||
041b39d2 XL |
934 | let low_index_mults = self.is_set(AS::LowIndexMultiplePositional) && |
935 | pos_counter == (self.positionals.len() - 1); | |
936 | let missing_pos = self.is_set(AS::AllowMissingPositional) && | |
937 | pos_counter == (self.positionals.len() - 1); | |
938 | debugln!("Parser::get_matches_with: Positional counter...{}", | |
939 | pos_counter); | |
940 | debugln!("Parser::get_matches_with: Low index multiples...{:?}", | |
941 | low_index_mults); | |
942 | if low_index_mults || missing_pos { | |
8bb4bdeb XL |
943 | if let Some(na) = it.peek() { |
944 | let n = (*na).clone().into(); | |
041b39d2 XL |
945 | needs_val_of = if needs_val_of != ParseResult::ValuesDone { |
946 | if let Some(p) = self.positionals.get(pos_counter) { | |
947 | ParseResult::Pos(p.b.name) | |
948 | } else { | |
949 | ParseResult::ValuesDone | |
950 | } | |
951 | } else { | |
952 | ParseResult::ValuesDone | |
953 | }; | |
954 | let sc_match = { | |
955 | self.possible_subcommand(&n).0 | |
956 | }; | |
957 | if self.is_new_arg(&n, needs_val_of) || sc_match || | |
958 | suggestions::did_you_mean(&n.to_string_lossy(), sc_names!(self)).is_some() { | |
959 | debugln!("Parser::get_matches_with: Bumping the positional counter..."); | |
8bb4bdeb XL |
960 | pos_counter += 1; |
961 | } | |
962 | } else { | |
041b39d2 | 963 | debugln!("Parser::get_matches_with: Bumping the positional counter..."); |
8bb4bdeb XL |
964 | pos_counter += 1; |
965 | } | |
041b39d2 XL |
966 | } else if self.is_set(AS::ContainsLast) && self.is_set(AS::TrailingValues) { |
967 | // Came to -- and one postional has .last(true) set, so we go immediately | |
968 | // to the last (highest index) positional | |
969 | debugln!("Parser::get_matches_with: .last(true) and --, setting last pos"); | |
970 | pos_counter = self.positionals.len(); | |
8bb4bdeb XL |
971 | } |
972 | if let Some(p) = self.positionals.get(pos_counter) { | |
041b39d2 XL |
973 | if p.is_set(ArgSettings::Last) && !self.is_set(AS::TrailingValues) { |
974 | return Err(Error::unknown_argument(&*arg_os.to_string_lossy(), | |
975 | "", | |
976 | &*usage::create_error_usage(self, | |
977 | matcher, | |
978 | None), | |
979 | self.color())); | |
980 | } | |
8bb4bdeb | 981 | parse_positional!(self, p, arg_os, pos_counter, matcher); |
041b39d2 XL |
982 | self.settings.set(AS::ValidArgFound); |
983 | } else if self.is_set(AS::AllowExternalSubcommands) { | |
8bb4bdeb XL |
984 | // Get external subcommand name |
985 | let sc_name = match arg_os.to_str() { | |
986 | Some(s) => s.to_string(), | |
987 | None => { | |
041b39d2 XL |
988 | if !self.is_set(AS::StrictUtf8) { |
989 | return Err(Error::invalid_utf8(&*usage::create_error_usage(self, | |
990 | matcher, | |
991 | None), | |
8bb4bdeb XL |
992 | self.color())); |
993 | } | |
994 | arg_os.to_string_lossy().into_owned() | |
995 | } | |
996 | }; | |
997 | ||
998 | // Collect the external subcommand args | |
999 | let mut sc_m = ArgMatcher::new(); | |
1000 | while let Some(v) = it.next() { | |
1001 | let a = v.into(); | |
041b39d2 XL |
1002 | if a.to_str().is_none() && !self.is_set(AS::StrictUtf8) { |
1003 | return Err(Error::invalid_utf8(&*usage::create_error_usage(self, | |
1004 | matcher, | |
1005 | None), | |
8bb4bdeb XL |
1006 | self.color())); |
1007 | } | |
1008 | sc_m.add_val_to("", &a); | |
1009 | } | |
1010 | ||
1011 | matcher.subcommand(SubCommand { | |
041b39d2 XL |
1012 | name: sc_name, |
1013 | matches: sc_m.into(), | |
1014 | }); | |
1015 | } else if !(self.is_set(AS::AllowLeadingHyphen) || | |
1016 | self.is_set(AS::AllowNegativeNumbers)) && | |
1017 | !self.is_set(AS::InferSubcommands) { | |
8bb4bdeb XL |
1018 | return Err(Error::unknown_argument(&*arg_os.to_string_lossy(), |
1019 | "", | |
041b39d2 XL |
1020 | &*usage::create_error_usage(self, |
1021 | matcher, | |
1022 | None), | |
8bb4bdeb | 1023 | self.color())); |
041b39d2 XL |
1024 | } else if !has_args || self.is_set(AS::InferSubcommands) && self.has_subcommands() { |
1025 | if let Some(cdate) = suggestions::did_you_mean(&*arg_os.to_string_lossy(), | |
1026 | sc_names!(self)) { | |
1027 | return Err(Error::invalid_subcommand(arg_os.to_string_lossy().into_owned(), | |
1028 | cdate, | |
1029 | self.meta | |
1030 | .bin_name | |
1031 | .as_ref() | |
1032 | .unwrap_or(&self.meta.name), | |
1033 | &*usage::create_error_usage(self, | |
1034 | matcher, | |
1035 | None), | |
1036 | self.color())); | |
1037 | } else { | |
1038 | return Err(Error::unrecognized_subcommand(arg_os | |
1039 | .to_string_lossy() | |
1040 | .into_owned(), | |
1041 | self.meta | |
1042 | .bin_name | |
1043 | .as_ref() | |
1044 | .unwrap_or(&self.meta.name), | |
1045 | self.color())); | |
1046 | } | |
8bb4bdeb XL |
1047 | } |
1048 | } | |
1049 | ||
1050 | if let Some(ref pos_sc_name) = subcmd_name { | |
041b39d2 XL |
1051 | let sc_name = { |
1052 | find_subcmd!(self, pos_sc_name) | |
1053 | .expect(INTERNAL_ERROR_MSG) | |
1054 | .p | |
1055 | .meta | |
1056 | .name | |
1057 | .clone() | |
cc61c64b | 1058 | }; |
3b2f2976 | 1059 | self.parse_subcommand(&*sc_name, matcher, it)?; |
041b39d2 | 1060 | } else if self.is_set(AS::SubcommandRequired) { |
7cac9316 | 1061 | let bn = self.meta.bin_name.as_ref().unwrap_or(&self.meta.name); |
8bb4bdeb | 1062 | return Err(Error::missing_subcommand(bn, |
041b39d2 | 1063 | &usage::create_error_usage(self, matcher, None), |
8bb4bdeb | 1064 | self.color())); |
041b39d2 XL |
1065 | } else if self.is_set(AS::SubcommandRequiredElseHelp) { |
1066 | debugln!("Parser::get_matches_with: SubcommandRequiredElseHelp=true"); | |
8bb4bdeb | 1067 | let mut out = vec![]; |
3b2f2976 | 1068 | self.write_help_err(&mut out)?; |
8bb4bdeb | 1069 | return Err(Error { |
041b39d2 XL |
1070 | message: String::from_utf8_lossy(&*out).into_owned(), |
1071 | kind: ErrorKind::MissingArgumentOrSubcommand, | |
1072 | info: None, | |
1073 | }); | |
8bb4bdeb XL |
1074 | } |
1075 | ||
041b39d2 | 1076 | Validator::new(self).validate(needs_val_of, subcmd_name, matcher) |
8bb4bdeb XL |
1077 | } |
1078 | ||
8bb4bdeb XL |
1079 | |
1080 | fn propogate_help_version(&mut self) { | |
041b39d2 | 1081 | debugln!("Parser::propogate_help_version;"); |
8bb4bdeb XL |
1082 | self.create_help_and_version(); |
1083 | for sc in &mut self.subcommands { | |
1084 | sc.p.propogate_help_version(); | |
1085 | } | |
1086 | } | |
1087 | ||
1088 | fn build_bin_names(&mut self) { | |
041b39d2 | 1089 | debugln!("Parser::build_bin_names;"); |
8bb4bdeb | 1090 | for sc in &mut self.subcommands { |
041b39d2 | 1091 | debug!("Parser::build_bin_names:iter: bin_name set..."); |
7cac9316 | 1092 | if sc.p.meta.bin_name.is_none() { |
8bb4bdeb XL |
1093 | sdebugln!("No"); |
1094 | let bin_name = format!("{}{}{}", | |
041b39d2 XL |
1095 | self.meta |
1096 | .bin_name | |
1097 | .as_ref() | |
1098 | .unwrap_or(&self.meta.name.clone()), | |
1099 | if self.meta.bin_name.is_some() { | |
1100 | " " | |
1101 | } else { | |
1102 | "" | |
1103 | }, | |
1104 | &*sc.p.meta.name); | |
1105 | debugln!("Parser::build_bin_names:iter: Setting bin_name of {} to {}", | |
1106 | self.meta.name, | |
1107 | bin_name); | |
8bb4bdeb XL |
1108 | sc.p.meta.bin_name = Some(bin_name); |
1109 | } else { | |
1110 | sdebugln!("yes ({:?})", sc.p.meta.bin_name); | |
1111 | } | |
041b39d2 XL |
1112 | debugln!("Parser::build_bin_names:iter: Calling build_bin_names from...{}", |
1113 | sc.p.meta.name); | |
8bb4bdeb XL |
1114 | sc.p.build_bin_names(); |
1115 | } | |
1116 | } | |
1117 | ||
1118 | fn parse_subcommand<I, T>(&mut self, | |
1119 | sc_name: &str, | |
1120 | matcher: &mut ArgMatcher<'a>, | |
1121 | it: &mut Peekable<I>) | |
1122 | -> ClapResult<()> | |
1123 | where I: Iterator<Item = T>, | |
1124 | T: Into<OsString> + Clone | |
1125 | { | |
1126 | use std::fmt::Write; | |
041b39d2 | 1127 | debugln!("Parser::parse_subcommand;"); |
8bb4bdeb | 1128 | let mut mid_string = String::new(); |
041b39d2 | 1129 | if !self.is_set(AS::SubcommandsNegateReqs) { |
7cac9316 | 1130 | let mut hs: Vec<&str> = self.required.iter().map(|n| &**n).collect(); |
8bb4bdeb XL |
1131 | for k in matcher.arg_names() { |
1132 | hs.push(k); | |
1133 | } | |
041b39d2 | 1134 | let reqs = usage::get_required_usage_from(self, &hs, Some(matcher), None, false); |
8bb4bdeb XL |
1135 | |
1136 | for s in &reqs { | |
1137 | write!(&mut mid_string, " {}", s).expect(INTERNAL_ERROR_MSG); | |
1138 | } | |
1139 | } | |
1140 | mid_string.push_str(" "); | |
7cac9316 | 1141 | if let Some(ref mut sc) = self.subcommands |
041b39d2 XL |
1142 | .iter_mut() |
1143 | .find(|s| s.p.meta.name == sc_name) { | |
8bb4bdeb XL |
1144 | let mut sc_matcher = ArgMatcher::new(); |
1145 | // bin_name should be parent's bin_name + [<reqs>] + the sc's name separated by | |
1146 | // a space | |
1147 | sc.p.meta.usage = Some(format!("{}{}{}", | |
7cac9316 | 1148 | self.meta.bin_name.as_ref().unwrap_or(&String::new()), |
8bb4bdeb XL |
1149 | if self.meta.bin_name.is_some() { |
1150 | &*mid_string | |
1151 | } else { | |
1152 | "" | |
1153 | }, | |
1154 | &*sc.p.meta.name)); | |
041b39d2 XL |
1155 | sc.p.meta.bin_name = |
1156 | Some(format!("{}{}{}", | |
1157 | self.meta.bin_name.as_ref().unwrap_or(&String::new()), | |
1158 | if self.meta.bin_name.is_some() { | |
1159 | " " | |
1160 | } else { | |
1161 | "" | |
1162 | }, | |
1163 | &*sc.p.meta.name)); | |
1164 | debugln!("Parser::parse_subcommand: About to parse sc={}", | |
1165 | sc.p.meta.name); | |
1166 | debugln!("Parser::parse_subcommand: sc settings={:#?}", sc.p.settings); | |
3b2f2976 | 1167 | sc.p.get_matches_with(&mut sc_matcher, it)?; |
8bb4bdeb | 1168 | matcher.subcommand(SubCommand { |
041b39d2 XL |
1169 | name: sc.p.meta.name.clone(), |
1170 | matches: sc_matcher.into(), | |
1171 | }); | |
8bb4bdeb XL |
1172 | } |
1173 | Ok(()) | |
1174 | } | |
1175 | ||
041b39d2 XL |
1176 | pub fn groups_for_arg(&self, name: &str) -> Option<Vec<&'a str>> { |
1177 | debugln!("Parser::groups_for_arg: name={}", name); | |
8bb4bdeb XL |
1178 | |
1179 | if self.groups.is_empty() { | |
041b39d2 | 1180 | debugln!("Parser::groups_for_arg: No groups defined"); |
8bb4bdeb XL |
1181 | return None; |
1182 | } | |
1183 | let mut res = vec![]; | |
041b39d2 XL |
1184 | debugln!("Parser::groups_for_arg: Searching through groups..."); |
1185 | for grp in &self.groups { | |
8bb4bdeb XL |
1186 | for a in &grp.args { |
1187 | if a == &name { | |
041b39d2 XL |
1188 | sdebugln!("\tFound '{}'", grp.name); |
1189 | res.push(&*grp.name); | |
8bb4bdeb XL |
1190 | } |
1191 | } | |
1192 | } | |
1193 | if res.is_empty() { | |
1194 | return None; | |
1195 | } | |
1196 | ||
1197 | Some(res) | |
1198 | } | |
1199 | ||
041b39d2 | 1200 | pub fn args_in_group(&self, group: &str) -> Vec<String> { |
8bb4bdeb XL |
1201 | let mut g_vec = vec![]; |
1202 | let mut args = vec![]; | |
1203 | ||
041b39d2 XL |
1204 | for n in &self.groups |
1205 | .iter() | |
1206 | .find(|g| g.name == group) | |
1207 | .expect(INTERNAL_ERROR_MSG) | |
1208 | .args { | |
8bb4bdeb XL |
1209 | if let Some(f) = self.flags.iter().find(|f| &f.b.name == n) { |
1210 | args.push(f.to_string()); | |
1211 | } else if let Some(f) = self.opts.iter().find(|o| &o.b.name == n) { | |
1212 | args.push(f.to_string()); | |
041b39d2 | 1213 | } else if let Some(p) = self.positionals.values().find(|p| &p.b.name == n) { |
7cac9316 | 1214 | args.push(p.b.name.to_owned()); |
041b39d2 XL |
1215 | } else { |
1216 | g_vec.push(*n); | |
8bb4bdeb XL |
1217 | } |
1218 | } | |
1219 | ||
1220 | for av in g_vec.iter().map(|g| self.args_in_group(g)) { | |
1221 | args.extend(av); | |
1222 | } | |
1223 | args.dedup(); | |
1224 | args.iter().map(ToOwned::to_owned).collect() | |
1225 | } | |
1226 | ||
041b39d2 | 1227 | pub fn arg_names_in_group(&self, group: &str) -> Vec<&'a str> { |
8bb4bdeb XL |
1228 | let mut g_vec = vec![]; |
1229 | let mut args = vec![]; | |
1230 | ||
041b39d2 XL |
1231 | for n in &self.groups |
1232 | .iter() | |
1233 | .find(|g| g.name == group) | |
1234 | .expect(INTERNAL_ERROR_MSG) | |
1235 | .args { | |
1236 | if self.groups.iter().any(|g| g.name == *n) { | |
1237 | args.extend(self.arg_names_in_group(n)); | |
8bb4bdeb | 1238 | g_vec.push(*n); |
041b39d2 | 1239 | } else if !args.contains(n) { |
7cac9316 | 1240 | args.push(*n); |
8bb4bdeb XL |
1241 | } |
1242 | } | |
1243 | ||
8bb4bdeb XL |
1244 | args.iter().map(|s| *s).collect() |
1245 | } | |
1246 | ||
1247 | pub fn create_help_and_version(&mut self) { | |
041b39d2 | 1248 | debugln!("Parser::create_help_and_version;"); |
8bb4bdeb | 1249 | // name is "hclap_help" because flags are sorted by name |
041b39d2 XL |
1250 | if !self.contains_long("help") { |
1251 | debugln!("Parser::create_help_and_version: Building --help"); | |
1252 | if self.help_short.is_none() && !self.contains_short('h') { | |
8bb4bdeb XL |
1253 | self.help_short = Some('h'); |
1254 | } | |
8bb4bdeb XL |
1255 | let arg = FlagBuilder { |
1256 | b: Base { | |
1257 | name: "hclap_help", | |
041b39d2 | 1258 | help: self.help_message.or(Some("Prints help information")), |
8bb4bdeb XL |
1259 | ..Default::default() |
1260 | }, | |
1261 | s: Switched { | |
1262 | short: self.help_short, | |
1263 | long: Some("help"), | |
1264 | ..Default::default() | |
1265 | }, | |
1266 | }; | |
041b39d2 | 1267 | self.flags.push(arg); |
8bb4bdeb | 1268 | } |
041b39d2 XL |
1269 | if !self.is_set(AS::DisableVersion) && !self.contains_long("version") { |
1270 | debugln!("Parser::create_help_and_version: Building --version"); | |
1271 | if self.version_short.is_none() && !self.contains_short('V') { | |
8bb4bdeb XL |
1272 | self.version_short = Some('V'); |
1273 | } | |
8bb4bdeb XL |
1274 | // name is "vclap_version" because flags are sorted by name |
1275 | let arg = FlagBuilder { | |
1276 | b: Base { | |
1277 | name: "vclap_version", | |
041b39d2 | 1278 | help: self.version_message.or(Some("Prints version information")), |
8bb4bdeb XL |
1279 | ..Default::default() |
1280 | }, | |
1281 | s: Switched { | |
1282 | short: self.version_short, | |
1283 | long: Some("version"), | |
1284 | ..Default::default() | |
1285 | }, | |
1286 | }; | |
041b39d2 | 1287 | self.flags.push(arg); |
8bb4bdeb | 1288 | } |
041b39d2 XL |
1289 | if !self.subcommands.is_empty() && !self.is_set(AS::DisableHelpSubcommand) && |
1290 | self.is_set(AS::NeedsSubcommandHelp) { | |
1291 | debugln!("Parser::create_help_and_version: Building help"); | |
8bb4bdeb XL |
1292 | self.subcommands |
1293 | .push(App::new("help") | |
041b39d2 | 1294 | .about("Prints this message or the help of the given subcommand(s)")); |
8bb4bdeb XL |
1295 | } |
1296 | } | |
1297 | ||
1298 | // Retrieves the names of all args the user has supplied thus far, except required ones | |
1299 | // because those will be listed in self.required | |
8bb4bdeb | 1300 | fn check_for_help_and_version_str(&self, arg: &OsStr) -> ClapResult<()> { |
041b39d2 XL |
1301 | debugln!("Parser::check_for_help_and_version_str;"); |
1302 | debug!("Parser::check_for_help_and_version_str: Checking if --{} is help or version...", | |
8bb4bdeb | 1303 | arg.to_str().unwrap()); |
041b39d2 | 1304 | if arg == "help" && self.is_set(AS::NeedsLongHelp) { |
8bb4bdeb | 1305 | sdebugln!("Help"); |
041b39d2 | 1306 | return Err(self._help(true)); |
8bb4bdeb | 1307 | } |
041b39d2 | 1308 | if arg == "version" && self.is_set(AS::NeedsLongVersion) { |
8bb4bdeb | 1309 | sdebugln!("Version"); |
041b39d2 | 1310 | return Err(self._version(true)); |
8bb4bdeb XL |
1311 | } |
1312 | sdebugln!("Neither"); | |
1313 | ||
1314 | Ok(()) | |
1315 | } | |
1316 | ||
1317 | fn check_for_help_and_version_char(&self, arg: char) -> ClapResult<()> { | |
041b39d2 XL |
1318 | debugln!("Parser::check_for_help_and_version_char;"); |
1319 | debug!("Parser::check_for_help_and_version_char: Checking if -{} is help or version...", | |
1320 | arg); | |
8bb4bdeb | 1321 | if let Some(h) = self.help_short { |
041b39d2 | 1322 | if arg == h && self.is_set(AS::NeedsLongHelp) { |
8bb4bdeb | 1323 | sdebugln!("Help"); |
041b39d2 | 1324 | return Err(self._help(false)); |
8bb4bdeb XL |
1325 | } |
1326 | } | |
1327 | if let Some(v) = self.version_short { | |
041b39d2 | 1328 | if arg == v && self.is_set(AS::NeedsLongVersion) { |
8bb4bdeb | 1329 | sdebugln!("Version"); |
041b39d2 | 1330 | return Err(self._version(false)); |
8bb4bdeb XL |
1331 | } |
1332 | } | |
1333 | sdebugln!("Neither"); | |
1334 | Ok(()) | |
1335 | } | |
1336 | ||
041b39d2 XL |
1337 | #[cfg_attr(feature = "cargo-clippy", allow(let_and_return))] |
1338 | fn use_long_help(&self) -> bool { | |
ea8adc8c XL |
1339 | let ul = self.meta.long_about.is_some() || |
1340 | self.flags.iter().any(|f| f.b.long_help.is_some()) || | |
041b39d2 XL |
1341 | self.opts.iter().any(|o| o.b.long_help.is_some()) || |
1342 | self.positionals.values().any(|p| p.b.long_help.is_some()) || | |
1343 | self.subcommands | |
1344 | .iter() | |
1345 | .any(|s| s.p.meta.long_about.is_some()); | |
1346 | debugln!("Parser::use_long_help: ret={:?}", ul); | |
1347 | ul | |
1348 | } | |
1349 | ||
1350 | fn _help(&self, mut use_long: bool) -> Error { | |
1351 | debugln!("Parser::_help: use_long={:?}", use_long); | |
1352 | use_long = use_long && self.use_long_help(); | |
8bb4bdeb | 1353 | let mut buf = vec![]; |
041b39d2 XL |
1354 | match Help::write_parser_help(&mut buf, self, use_long) { |
1355 | Err(e) => e, | |
1356 | _ => Error { | |
1357 | message: unsafe { String::from_utf8_unchecked(buf) }, | |
1358 | kind: ErrorKind::HelpDisplayed, | |
1359 | info: None, | |
1360 | } | |
1361 | } | |
8bb4bdeb XL |
1362 | } |
1363 | ||
041b39d2 XL |
1364 | fn _version(&self, use_long: bool) -> Error { |
1365 | debugln!("Parser::_version: "); | |
8bb4bdeb XL |
1366 | let out = io::stdout(); |
1367 | let mut buf_w = BufWriter::new(out.lock()); | |
041b39d2 XL |
1368 | match self.print_version(&mut buf_w, use_long) { |
1369 | Err(e) => e, | |
1370 | _ => Error { | |
1371 | message: String::new(), | |
1372 | kind: ErrorKind::VersionDisplayed, | |
1373 | info: None, | |
1374 | } | |
1375 | } | |
8bb4bdeb XL |
1376 | } |
1377 | ||
1378 | fn parse_long_arg(&mut self, | |
1379 | matcher: &mut ArgMatcher<'a>, | |
1380 | full_arg: &OsStr) | |
041b39d2 | 1381 | -> ClapResult<ParseResult<'a>> { |
8bb4bdeb | 1382 | // maybe here lifetime should be 'a |
041b39d2 | 1383 | debugln!("Parser::parse_long_arg;"); |
8bb4bdeb | 1384 | let mut val = None; |
041b39d2 | 1385 | debug!("Parser::parse_long_arg: Does it contain '='..."); |
8bb4bdeb XL |
1386 | let arg = if full_arg.contains_byte(b'=') { |
1387 | let (p0, p1) = full_arg.trim_left_matches(b'-').split_at_byte(b'='); | |
1388 | sdebugln!("Yes '{:?}'", p1); | |
1389 | val = Some(p1); | |
1390 | p0 | |
1391 | } else { | |
1392 | sdebugln!("No"); | |
1393 | full_arg.trim_left_matches(b'-') | |
1394 | }; | |
1395 | ||
041b39d2 XL |
1396 | if let Some(opt) = find_opt_by_long!(@os self, arg) { |
1397 | debugln!("Parser::parse_long_arg: Found valid opt '{}'", | |
1398 | opt.to_string()); | |
1399 | self.settings.set(AS::ValidArgFound); | |
3b2f2976 | 1400 | let ret = self.parse_opt(val, opt, val.is_some(), matcher)?; |
041b39d2 XL |
1401 | if self.cache.map_or(true, |name| name != opt.b.name) { |
1402 | arg_post_processing!(self, opt, matcher); | |
1403 | self.cache = Some(opt.b.name); | |
1404 | } | |
8bb4bdeb XL |
1405 | |
1406 | return Ok(ret); | |
041b39d2 XL |
1407 | } else if let Some(flag) = find_flag_by_long!(@os self, arg) { |
1408 | debugln!("Parser::parse_long_arg: Found valid flag '{}'", | |
1409 | flag.to_string()); | |
1410 | self.settings.set(AS::ValidArgFound); | |
8bb4bdeb XL |
1411 | // Only flags could be help or version, and we need to check the raw long |
1412 | // so this is the first point to check | |
3b2f2976 | 1413 | self.check_for_help_and_version_str(arg)?; |
8bb4bdeb | 1414 | |
3b2f2976 | 1415 | self.parse_flag(flag, matcher)?; |
8bb4bdeb XL |
1416 | |
1417 | // Handle conflicts, requirements, etc. | |
041b39d2 | 1418 | // if self.cache.map_or(true, |name| name != flag.b.name) { |
7cac9316 | 1419 | arg_post_processing!(self, flag, matcher); |
041b39d2 XL |
1420 | // self.cache = Some(flag.b.name); |
1421 | // } | |
8bb4bdeb | 1422 | |
041b39d2 XL |
1423 | return Ok(ParseResult::Flag); |
1424 | } else if self.is_set(AS::AllowLeadingHyphen) { | |
1425 | return Ok(ParseResult::MaybeHyphenValue); | |
1426 | } else if self.is_set(AS::ValidNegNumFound) { | |
1427 | return Ok(ParseResult::MaybeNegNum); | |
8bb4bdeb XL |
1428 | } |
1429 | ||
041b39d2 XL |
1430 | debugln!("Parser::parse_long_arg: Didn't match anything"); |
1431 | self.did_you_mean_error(arg.to_str().expect(INVALID_UTF8), matcher) | |
1432 | .map(|_| ParseResult::NotFound) | |
8bb4bdeb XL |
1433 | } |
1434 | ||
041b39d2 | 1435 | #[cfg_attr(feature = "lints", allow(len_zero))] |
8bb4bdeb XL |
1436 | fn parse_short_arg(&mut self, |
1437 | matcher: &mut ArgMatcher<'a>, | |
1438 | full_arg: &OsStr) | |
041b39d2 XL |
1439 | -> ClapResult<ParseResult<'a>> { |
1440 | debugln!("Parser::parse_short_arg: full_arg={:?}", full_arg); | |
8bb4bdeb XL |
1441 | let arg_os = full_arg.trim_left_matches(b'-'); |
1442 | let arg = arg_os.to_string_lossy(); | |
1443 | ||
041b39d2 XL |
1444 | // If AllowLeadingHyphen is set, we want to ensure `-val` gets parsed as `-val` and not |
1445 | // `-v` `-a` `-l` assuming `v` `a` and `l` are all, or mostly, valid shorts. | |
1446 | if self.is_set(AS::AllowLeadingHyphen) { | |
1447 | if arg.chars().any(|c| !self.contains_short(c)) { | |
1448 | debugln!("Parser::parse_short_arg: LeadingHyphenAllowed yet -{} isn't valid", | |
1449 | arg); | |
1450 | return Ok(ParseResult::MaybeHyphenValue); | |
8bb4bdeb | 1451 | } |
041b39d2 | 1452 | } else if self.is_set(AS::ValidNegNumFound) { |
8bb4bdeb XL |
1453 | // TODO: Add docs about having AllowNegativeNumbers and `-2` as a valid short |
1454 | // May be better to move this to *after* not finding a valid flag/opt? | |
041b39d2 XL |
1455 | debugln!("Parser::parse_short_arg: Valid negative num..."); |
1456 | return Ok(ParseResult::MaybeNegNum); | |
8bb4bdeb XL |
1457 | } |
1458 | ||
041b39d2 | 1459 | let mut ret = ParseResult::NotFound; |
8bb4bdeb | 1460 | for c in arg.chars() { |
041b39d2 | 1461 | debugln!("Parser::parse_short_arg:iter:{}", c); |
8bb4bdeb XL |
1462 | // Check for matching short options, and return the name if there is no trailing |
1463 | // concatenated value: -oval | |
1464 | // Option: -o | |
1465 | // Value: val | |
041b39d2 XL |
1466 | if let Some(opt) = find_opt_by_short!(self, c) { |
1467 | debugln!("Parser::parse_short_arg:iter:{}: Found valid opt", c); | |
1468 | self.settings.set(AS::ValidArgFound); | |
8bb4bdeb XL |
1469 | // Check for trailing concatenated value |
1470 | let p: Vec<_> = arg.splitn(2, c).collect(); | |
041b39d2 XL |
1471 | debugln!("Parser::parse_short_arg:iter:{}: p[0]={:?}, p[1]={:?}", |
1472 | c, | |
1473 | p[0].as_bytes(), | |
1474 | p[1].as_bytes()); | |
8bb4bdeb XL |
1475 | let i = p[0].as_bytes().len() + 1; |
1476 | let val = if p[1].as_bytes().len() > 0 { | |
041b39d2 XL |
1477 | debugln!("Parser::parse_short_arg:iter:{}: val={:?} (bytes), val={:?} (ascii)", |
1478 | c, | |
8bb4bdeb XL |
1479 | arg_os.split_at(i).1.as_bytes(), |
1480 | arg_os.split_at(i).1); | |
1481 | Some(arg_os.split_at(i).1) | |
1482 | } else { | |
1483 | None | |
1484 | }; | |
1485 | ||
1486 | // Default to "we're expecting a value later" | |
3b2f2976 | 1487 | let ret = self.parse_opt(val, opt, false, matcher)?; |
8bb4bdeb | 1488 | |
041b39d2 XL |
1489 | if self.cache.map_or(true, |name| name != opt.b.name) { |
1490 | arg_post_processing!(self, opt, matcher); | |
1491 | self.cache = Some(opt.b.name); | |
1492 | } | |
8bb4bdeb XL |
1493 | |
1494 | return Ok(ret); | |
041b39d2 XL |
1495 | } else if let Some(flag) = find_flag_by_short!(self, c) { |
1496 | debugln!("Parser::parse_short_arg:iter:{}: Found valid flag", c); | |
1497 | self.settings.set(AS::ValidArgFound); | |
8bb4bdeb | 1498 | // Only flags can be help or version |
3b2f2976 XL |
1499 | self.check_for_help_and_version_char(c)?; |
1500 | ret = self.parse_flag(flag, matcher)?; | |
041b39d2 | 1501 | |
8bb4bdeb XL |
1502 | // Handle conflicts, requirements, overrides, etc. |
1503 | // Must be called here due to mutablilty | |
041b39d2 XL |
1504 | if self.cache.map_or(true, |name| name != flag.b.name) { |
1505 | arg_post_processing!(self, flag, matcher); | |
1506 | self.cache = Some(flag.b.name); | |
1507 | } | |
8bb4bdeb | 1508 | } else { |
041b39d2 | 1509 | let arg = format!("-{}", c); |
8bb4bdeb XL |
1510 | return Err(Error::unknown_argument(&*arg, |
1511 | "", | |
041b39d2 XL |
1512 | &*usage::create_error_usage(self, |
1513 | matcher, | |
1514 | None), | |
8bb4bdeb XL |
1515 | self.color())); |
1516 | } | |
1517 | } | |
041b39d2 | 1518 | Ok(ret) |
8bb4bdeb XL |
1519 | } |
1520 | ||
1521 | fn parse_opt(&self, | |
1522 | val: Option<&OsStr>, | |
1523 | opt: &OptBuilder<'a, 'b>, | |
041b39d2 | 1524 | had_eq: bool, |
8bb4bdeb | 1525 | matcher: &mut ArgMatcher<'a>) |
041b39d2 XL |
1526 | -> ClapResult<ParseResult<'a>> { |
1527 | debugln!("Parser::parse_opt; opt={}, val={:?}", opt.b.name, val); | |
1528 | debugln!("Parser::parse_opt; opt.settings={:?}", opt.b.settings); | |
8bb4bdeb | 1529 | let mut has_eq = false; |
ea8adc8c XL |
1530 | let no_val = val.is_none(); |
1531 | let empty_vals = opt.is_set(ArgSettings::EmptyValues); | |
1532 | let min_vals_zero = opt.v.min_vals.unwrap_or(1) == 0; | |
1533 | let needs_eq = opt.is_set(ArgSettings::RequireEquals); | |
8bb4bdeb | 1534 | |
041b39d2 | 1535 | debug!("Parser::parse_opt; Checking for val..."); |
8bb4bdeb | 1536 | if let Some(fv) = val { |
041b39d2 | 1537 | has_eq = fv.starts_with(&[b'=']) || had_eq; |
8bb4bdeb | 1538 | let v = fv.trim_left_matches(b'='); |
ea8adc8c XL |
1539 | if !empty_vals && |
1540 | (v.len_() == 0 || (needs_eq && !has_eq)) { | |
8bb4bdeb XL |
1541 | sdebugln!("Found Empty - Error"); |
1542 | return Err(Error::empty_value(opt, | |
041b39d2 | 1543 | &*usage::create_error_usage(self, matcher, None), |
8bb4bdeb XL |
1544 | self.color())); |
1545 | } | |
1546 | sdebugln!("Found - {:?}, len: {}", v, v.len_()); | |
041b39d2 XL |
1547 | debugln!("Parser::parse_opt: {:?} contains '='...{:?}", |
1548 | fv, | |
1549 | fv.starts_with(&[b'='])); | |
3b2f2976 | 1550 | self.add_val_to_arg(opt, v, matcher)?; |
ea8adc8c | 1551 | } else if needs_eq && !(empty_vals || min_vals_zero) { |
041b39d2 XL |
1552 | sdebugln!("None, but requires equals...Error"); |
1553 | return Err(Error::empty_value(opt, | |
1554 | &*usage::create_error_usage(self, matcher, None), | |
1555 | self.color())); | |
1556 | ||
8bb4bdeb XL |
1557 | } else { |
1558 | sdebugln!("None"); | |
1559 | } | |
1560 | ||
1561 | matcher.inc_occurrence_of(opt.b.name); | |
1562 | // Increment or create the group "args" | |
041b39d2 XL |
1563 | self.groups_for_arg(opt.b.name) |
1564 | .and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); | |
8bb4bdeb | 1565 | |
ea8adc8c XL |
1566 | let needs_delim = opt.is_set(ArgSettings::RequireDelimiter); |
1567 | let mult = opt.is_set(ArgSettings::Multiple); | |
1568 | if no_val && min_vals_zero && !has_eq && needs_eq { | |
1569 | debugln!("Parser::parse_opt: More arg vals not required..."); | |
1570 | return Ok(ParseResult::ValuesDone); | |
1571 | } else if no_val || (mult && !needs_delim) && !has_eq && matcher.needs_more_vals(opt) { | |
041b39d2 XL |
1572 | debugln!("Parser::parse_opt: More arg vals required..."); |
1573 | return Ok(ParseResult::Opt(opt.b.name)); | |
8bb4bdeb | 1574 | } |
041b39d2 XL |
1575 | debugln!("Parser::parse_opt: More arg vals not required..."); |
1576 | Ok(ParseResult::ValuesDone) | |
8bb4bdeb XL |
1577 | } |
1578 | ||
1579 | fn add_val_to_arg<A>(&self, | |
1580 | arg: &A, | |
1581 | val: &OsStr, | |
1582 | matcher: &mut ArgMatcher<'a>) | |
041b39d2 | 1583 | -> ClapResult<ParseResult<'a>> |
8bb4bdeb XL |
1584 | where A: AnyArg<'a, 'b> + Display |
1585 | { | |
041b39d2 XL |
1586 | debugln!("Parser::add_val_to_arg; arg={}, val={:?}", arg.name(), val); |
1587 | debugln!("Parser::add_val_to_arg; trailing_vals={:?}, DontDelimTrailingVals={:?}", | |
1588 | self.is_set(AS::TrailingValues), | |
1589 | self.is_set(AS::DontDelimitTrailingValues)); | |
1590 | if !(self.is_set(AS::TrailingValues) && self.is_set(AS::DontDelimitTrailingValues)) { | |
8bb4bdeb XL |
1591 | if let Some(delim) = arg.val_delim() { |
1592 | if val.is_empty_() { | |
3b2f2976 | 1593 | Ok(self.add_single_val_to_arg(arg, val, matcher)?) |
8bb4bdeb | 1594 | } else { |
041b39d2 | 1595 | let mut iret = ParseResult::ValuesDone; |
8bb4bdeb | 1596 | for v in val.split(delim as u32 as u8) { |
3b2f2976 | 1597 | iret = self.add_single_val_to_arg(arg, v, matcher)?; |
8bb4bdeb XL |
1598 | } |
1599 | // If there was a delimiter used, we're not looking for more values | |
1600 | if val.contains_byte(delim as u32 as u8) || | |
041b39d2 XL |
1601 | arg.is_set(ArgSettings::RequireDelimiter) { |
1602 | iret = ParseResult::ValuesDone; | |
1603 | } | |
1604 | Ok(iret) | |
8bb4bdeb XL |
1605 | } |
1606 | } else { | |
041b39d2 | 1607 | self.add_single_val_to_arg(arg, val, matcher) |
8bb4bdeb XL |
1608 | } |
1609 | } else { | |
041b39d2 | 1610 | self.add_single_val_to_arg(arg, val, matcher) |
8bb4bdeb | 1611 | } |
8bb4bdeb XL |
1612 | } |
1613 | ||
1614 | fn add_single_val_to_arg<A>(&self, | |
1615 | arg: &A, | |
1616 | v: &OsStr, | |
1617 | matcher: &mut ArgMatcher<'a>) | |
041b39d2 | 1618 | -> ClapResult<ParseResult<'a>> |
8bb4bdeb XL |
1619 | where A: AnyArg<'a, 'b> + Display |
1620 | { | |
041b39d2 XL |
1621 | debugln!("Parser::add_single_val_to_arg;"); |
1622 | debugln!("Parser::add_single_val_to_arg: adding val...{:?}", v); | |
1623 | if let Some(t) = arg.val_terminator() { | |
1624 | if t == v { | |
1625 | return Ok(ParseResult::ValuesDone); | |
1626 | } | |
1627 | } | |
8bb4bdeb XL |
1628 | matcher.add_val_to(arg.name(), v); |
1629 | ||
1630 | // Increment or create the group "args" | |
1631 | if let Some(grps) = self.groups_for_arg(arg.name()) { | |
1632 | for grp in grps { | |
1633 | matcher.add_val_to(&*grp, v); | |
1634 | } | |
1635 | } | |
1636 | ||
8bb4bdeb | 1637 | if matcher.needs_more_vals(arg) { |
041b39d2 | 1638 | return Ok(ParseResult::Opt(arg.name())); |
8bb4bdeb | 1639 | } |
041b39d2 | 1640 | Ok(ParseResult::ValuesDone) |
8bb4bdeb XL |
1641 | } |
1642 | ||
041b39d2 | 1643 | |
8bb4bdeb XL |
1644 | fn parse_flag(&self, |
1645 | flag: &FlagBuilder<'a, 'b>, | |
1646 | matcher: &mut ArgMatcher<'a>) | |
041b39d2 XL |
1647 | -> ClapResult<ParseResult<'a>> { |
1648 | debugln!("Parser::parse_flag;"); | |
8bb4bdeb XL |
1649 | |
1650 | matcher.inc_occurrence_of(flag.b.name); | |
1651 | // Increment or create the group "args" | |
041b39d2 XL |
1652 | self.groups_for_arg(flag.b.name) |
1653 | .and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); | |
7cac9316 | 1654 | |
041b39d2 | 1655 | Ok(ParseResult::Flag) |
7cac9316 XL |
1656 | } |
1657 | ||
8bb4bdeb | 1658 | fn did_you_mean_error(&self, arg: &str, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> { |
041b39d2 XL |
1659 | |
1660 | // Didn't match a flag or option | |
8bb4bdeb | 1661 | let suffix = |
041b39d2 | 1662 | suggestions::did_you_mean_flag_suffix(arg, longs!(self), &self.subcommands); |
8bb4bdeb XL |
1663 | |
1664 | // Add the arg to the matches to build a proper usage string | |
1665 | if let Some(name) = suffix.1 { | |
041b39d2 | 1666 | if let Some(opt) = find_opt_by_long!(self, name) { |
8bb4bdeb XL |
1667 | self.groups_for_arg(&*opt.b.name) |
1668 | .and_then(|grps| Some(matcher.inc_occurrences_of(&*grps))); | |
1669 | matcher.insert(&*opt.b.name); | |
041b39d2 | 1670 | } else if let Some(flg) = find_flag_by_long!(self, name) { |
8bb4bdeb XL |
1671 | self.groups_for_arg(&*flg.b.name) |
1672 | .and_then(|grps| Some(matcher.inc_occurrences_of(&*grps))); | |
1673 | matcher.insert(&*flg.b.name); | |
1674 | } | |
1675 | } | |
1676 | ||
1677 | let used_arg = format!("--{}", arg); | |
1678 | Err(Error::unknown_argument(&*used_arg, | |
1679 | &*suffix.0, | |
041b39d2 | 1680 | &*usage::create_error_usage(self, matcher, None), |
8bb4bdeb XL |
1681 | self.color())) |
1682 | } | |
1683 | ||
8bb4bdeb | 1684 | // Prints the version to the user and exits if quit=true |
041b39d2 | 1685 | fn print_version<W: Write>(&self, w: &mut W, use_long: bool) -> ClapResult<()> { |
3b2f2976 | 1686 | self.write_version(w, use_long)?; |
8bb4bdeb XL |
1687 | w.flush().map_err(Error::from) |
1688 | } | |
1689 | ||
041b39d2 XL |
1690 | pub fn write_version<W: Write>(&self, w: &mut W, use_long: bool) -> io::Result<()> { |
1691 | let ver = if use_long { | |
1692 | self.meta | |
1693 | .long_version | |
1694 | .unwrap_or_else(|| self.meta.version.unwrap_or("")) | |
1695 | } else { | |
1696 | self.meta | |
1697 | .version | |
1698 | .unwrap_or_else(|| self.meta.long_version.unwrap_or("")) | |
1699 | }; | |
8bb4bdeb XL |
1700 | if let Some(bn) = self.meta.bin_name.as_ref() { |
1701 | if bn.contains(' ') { | |
1702 | // Incase we're dealing with subcommands i.e. git mv is translated to git-mv | |
041b39d2 | 1703 | write!(w, "{} {}", bn.replace(" ", "-"), ver) |
8bb4bdeb | 1704 | } else { |
041b39d2 | 1705 | write!(w, "{} {}", &self.meta.name[..], ver) |
8bb4bdeb XL |
1706 | } |
1707 | } else { | |
041b39d2 | 1708 | write!(w, "{} {}", &self.meta.name[..], ver) |
8bb4bdeb XL |
1709 | } |
1710 | } | |
1711 | ||
1712 | pub fn print_help(&self) -> ClapResult<()> { | |
1713 | let out = io::stdout(); | |
1714 | let mut buf_w = BufWriter::new(out.lock()); | |
1715 | self.write_help(&mut buf_w) | |
1716 | } | |
1717 | ||
1718 | pub fn write_help<W: Write>(&self, w: &mut W) -> ClapResult<()> { | |
041b39d2 XL |
1719 | Help::write_parser_help(w, self, false) |
1720 | } | |
1721 | ||
1722 | pub fn write_long_help<W: Write>(&self, w: &mut W) -> ClapResult<()> { | |
1723 | Help::write_parser_help(w, self, true) | |
8bb4bdeb XL |
1724 | } |
1725 | ||
1726 | pub fn write_help_err<W: Write>(&self, w: &mut W) -> ClapResult<()> { | |
1727 | Help::write_parser_help_to_stderr(w, self) | |
1728 | } | |
1729 | ||
041b39d2 | 1730 | pub fn add_defaults(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> { |
8bb4bdeb | 1731 | macro_rules! add_val { |
041b39d2 XL |
1732 | (@default $_self:ident, $a:ident, $m:ident) => { |
1733 | if let Some(ref val) = $a.v.default_val { | |
ea8adc8c XL |
1734 | if $m.get($a.b.name).map(|ma| ma.vals.len()).map(|len| len == 0).unwrap_or(false) { |
1735 | $_self.add_val_to_arg($a, OsStr::new(val), $m)?; | |
1736 | ||
1737 | if $_self.cache.map_or(true, |name| name != $a.name()) { | |
1738 | arg_post_processing!($_self, $a, $m); | |
1739 | $_self.cache = Some($a.name()); | |
1740 | } | |
1741 | } else { | |
3b2f2976 | 1742 | $_self.add_val_to_arg($a, OsStr::new(val), $m)?; |
041b39d2 XL |
1743 | |
1744 | if $_self.cache.map_or(true, |name| name != $a.name()) { | |
1745 | arg_post_processing!($_self, $a, $m); | |
1746 | $_self.cache = Some($a.name()); | |
1747 | } | |
1748 | } | |
1749 | } | |
1750 | }; | |
8bb4bdeb | 1751 | ($_self:ident, $a:ident, $m:ident) => { |
041b39d2 XL |
1752 | if let Some(ref vm) = $a.v.default_vals_ifs { |
1753 | let mut done = false; | |
1754 | if $m.get($a.b.name).is_none() { | |
1755 | for &(arg, val, default) in vm.values() { | |
1756 | let add = if let Some(a) = $m.get(arg) { | |
1757 | if let Some(v) = val { | |
1758 | a.vals.iter().any(|value| v == value) | |
1759 | } else { | |
1760 | true | |
1761 | } | |
1762 | } else { | |
1763 | false | |
1764 | }; | |
1765 | if add { | |
3b2f2976 | 1766 | $_self.add_val_to_arg($a, OsStr::new(default), $m)?; |
041b39d2 XL |
1767 | if $_self.cache.map_or(true, |name| name != $a.name()) { |
1768 | arg_post_processing!($_self, $a, $m); | |
1769 | $_self.cache = Some($a.name()); | |
1770 | } | |
1771 | done = true; | |
1772 | break; | |
1773 | } | |
1774 | } | |
1775 | } | |
1776 | ||
1777 | if done { | |
1778 | continue; // outer loop (outside macro) | |
1779 | } | |
8bb4bdeb | 1780 | } |
041b39d2 | 1781 | add_val!(@default $_self, $a, $m) |
8bb4bdeb XL |
1782 | }; |
1783 | } | |
041b39d2 XL |
1784 | |
1785 | for o in &self.opts { | |
8bb4bdeb XL |
1786 | add_val!(self, o, matcher); |
1787 | } | |
041b39d2 | 1788 | for p in self.positionals.values() { |
8bb4bdeb XL |
1789 | add_val!(self, p, matcher); |
1790 | } | |
1791 | Ok(()) | |
1792 | } | |
1793 | ||
1794 | pub fn flags(&self) -> Iter<FlagBuilder<'a, 'b>> { self.flags.iter() } | |
1795 | ||
1796 | pub fn opts(&self) -> Iter<OptBuilder<'a, 'b>> { self.opts.iter() } | |
1797 | ||
1798 | pub fn positionals(&self) -> vec_map::Values<PosBuilder<'a, 'b>> { self.positionals.values() } | |
1799 | ||
1800 | pub fn subcommands(&self) -> Iter<App> { self.subcommands.iter() } | |
1801 | ||
1802 | // Should we color the output? None=determined by output location, true=yes, false=no | |
1803 | #[doc(hidden)] | |
1804 | pub fn color(&self) -> ColorWhen { | |
041b39d2 XL |
1805 | debugln!("Parser::color;"); |
1806 | debug!("Parser::color: Color setting..."); | |
1807 | if self.is_set(AS::ColorNever) { | |
8bb4bdeb XL |
1808 | sdebugln!("Never"); |
1809 | ColorWhen::Never | |
041b39d2 | 1810 | } else if self.is_set(AS::ColorAlways) { |
8bb4bdeb XL |
1811 | sdebugln!("Always"); |
1812 | ColorWhen::Always | |
1813 | } else { | |
1814 | sdebugln!("Auto"); | |
1815 | ColorWhen::Auto | |
1816 | } | |
1817 | } | |
1818 | ||
1819 | pub fn find_any_arg(&self, name: &str) -> Option<&AnyArg> { | |
041b39d2 | 1820 | if let Some(f) = find_by_name!(self, name, flags, iter) { |
8bb4bdeb XL |
1821 | return Some(f); |
1822 | } | |
041b39d2 | 1823 | if let Some(o) = find_by_name!(self, name, opts, iter) { |
8bb4bdeb XL |
1824 | return Some(o); |
1825 | } | |
041b39d2 | 1826 | if let Some(p) = find_by_name!(self, name, positionals, values) { |
8bb4bdeb XL |
1827 | return Some(p); |
1828 | } | |
1829 | None | |
1830 | } | |
1831 | ||
041b39d2 XL |
1832 | /// Check is a given string matches the binary name for this parser |
1833 | fn is_bin_name(&self, value: &str) -> bool { | |
1834 | self.meta.bin_name | |
1835 | .as_ref() | |
1836 | .and_then(|name| Some(value == name)) | |
1837 | .unwrap_or(false) | |
1838 | } | |
1839 | ||
1840 | /// Check is a given string is an alias for this parser | |
1841 | fn is_alias(&self, value: &str) -> bool { | |
1842 | self.meta.aliases | |
1843 | .as_ref() | |
1844 | .and_then(|aliases| { | |
1845 | for alias in aliases { | |
1846 | if alias.0 == value { | |
1847 | return Some(true); | |
1848 | } | |
1849 | } | |
1850 | Some(false) | |
1851 | }) | |
1852 | .unwrap_or(false) | |
1853 | } | |
1854 | ||
1855 | // Only used for completion scripts due to bin_name messiness | |
1856 | #[cfg_attr(feature = "lints", allow(block_in_if_condition_stmt))] | |
8bb4bdeb | 1857 | pub fn find_subcommand(&'b self, sc: &str) -> Option<&'b App<'a, 'b>> { |
041b39d2 XL |
1858 | debugln!("Parser::find_subcommand: sc={}", sc); |
1859 | debugln!("Parser::find_subcommand: Currently in Parser...{}", | |
1860 | self.meta.bin_name.as_ref().unwrap()); | |
1861 | for s in &self.subcommands { | |
1862 | if s.p.is_bin_name(sc) { | |
1863 | return Some(s); | |
1864 | } | |
1865 | // XXX: why do we split here? | |
1866 | // isn't `sc` supposed to be single word already? | |
1867 | let last = sc.split(' ').rev().next().expect(INTERNAL_ERROR_MSG); | |
1868 | if s.p.is_alias(last) { | |
8bb4bdeb XL |
1869 | return Some(s); |
1870 | } | |
041b39d2 | 1871 | |
8bb4bdeb XL |
1872 | if let Some(app) = s.p.find_subcommand(sc) { |
1873 | return Some(app); | |
1874 | } | |
1875 | } | |
1876 | None | |
1877 | } | |
8bb4bdeb | 1878 | |
041b39d2 XL |
1879 | #[inline] |
1880 | fn contains_long(&self, l: &str) -> bool { longs!(self).any(|al| al == &l) } | |
1881 | ||
1882 | #[inline] | |
1883 | fn contains_short(&self, s: char) -> bool { shorts!(self).any(|arg_s| arg_s == &s) } | |
8bb4bdeb | 1884 | } |