| // Std |
| #[cfg(all(feature = "debug", any(target_os = "windows", target_arch = "wasm32")))] |
| use osstringext::OsStrExt3; |
| use std::cell::Cell; |
| use std::ffi::{OsStr, OsString}; |
| use std::fmt::Display; |
| use std::fs::File; |
| use std::io::{self, BufWriter, Write}; |
| use std::iter::Peekable; |
| #[cfg(all( |
| feature = "debug", |
| not(any(target_os = "windows", target_arch = "wasm32")) |
| ))] |
| use std::os::unix::ffi::OsStrExt; |
| use std::path::PathBuf; |
| use std::slice::Iter; |
| |
| // Internal |
| use app::help::Help; |
| use app::meta::AppMeta; |
| use app::settings::AppFlags; |
| use app::settings::AppSettings as AS; |
| use app::usage; |
| use app::validator::Validator; |
| use app::App; |
| use args::settings::ArgSettings; |
| use args::{ |
| AnyArg, Arg, ArgGroup, ArgMatcher, Base, FlagBuilder, OptBuilder, PosBuilder, Switched, |
| }; |
| use completions::ComplGen; |
| use completions::Shell; |
| use errors::Result as ClapResult; |
| use errors::{Error, ErrorKind}; |
| use fmt::ColorWhen; |
| use map::{self, VecMap}; |
| use osstringext::OsStrExt2; |
| use suggestions; |
| use SubCommand; |
| use INTERNAL_ERROR_MSG; |
| use INVALID_UTF8; |
| |
| #[derive(Debug, PartialEq, Copy, Clone)] |
| #[doc(hidden)] |
| pub enum ParseResult<'a> { |
| Flag, |
| Opt(&'a str), |
| Pos(&'a str), |
| MaybeHyphenValue, |
| MaybeNegNum, |
| NotFound, |
| ValuesDone, |
| } |
| |
| #[allow(missing_debug_implementations)] |
| #[doc(hidden)] |
| #[derive(Clone, Default)] |
| pub struct Parser<'a, 'b> |
| where |
| 'a: 'b, |
| { |
| pub meta: AppMeta<'b>, |
| settings: AppFlags, |
| pub g_settings: AppFlags, |
| pub flags: Vec<FlagBuilder<'a, 'b>>, |
| pub opts: Vec<OptBuilder<'a, 'b>>, |
| pub positionals: VecMap<PosBuilder<'a, 'b>>, |
| pub subcommands: Vec<App<'a, 'b>>, |
| pub groups: Vec<ArgGroup<'a>>, |
| pub global_args: Vec<Arg<'a, 'b>>, |
| pub required: Vec<&'a str>, |
| pub r_ifs: Vec<(&'a str, &'b str, &'a str)>, |
| pub overrides: Vec<(&'b str, &'a str)>, |
| help_short: Option<char>, |
| version_short: Option<char>, |
| cache: Option<&'a str>, |
| pub help_message: Option<&'a str>, |
| pub version_message: Option<&'a str>, |
| cur_idx: Cell<usize>, |
| } |
| |
| impl<'a, 'b> Parser<'a, 'b> |
| where |
| 'a: 'b, |
| { |
| pub fn with_name(n: String) -> Self { |
| Parser { |
| meta: AppMeta::with_name(n), |
| g_settings: AppFlags::zeroed(), |
| cur_idx: Cell::new(0), |
| ..Default::default() |
| } |
| } |
| |
| pub fn help_short(&mut self, s: &str) { |
| let c = s |
| .trim_left_matches(|c| c == '-') |
| .chars() |
| .nth(0) |
| .unwrap_or('h'); |
| self.help_short = Some(c); |
| } |
| |
| pub fn version_short(&mut self, s: &str) { |
| let c = s |
| .trim_left_matches(|c| c == '-') |
| .chars() |
| .nth(0) |
| .unwrap_or('V'); |
| self.version_short = Some(c); |
| } |
| |
| pub fn gen_completions_to<W: Write>(&mut self, for_shell: Shell, buf: &mut W) { |
| if !self.is_set(AS::Propagated) { |
| self.propagate_help_version(); |
| self.build_bin_names(); |
| self.propagate_globals(); |
| self.propagate_settings(); |
| self.set(AS::Propagated); |
| } |
| |
| ComplGen::new(self).generate(for_shell, buf) |
| } |
| |
| pub fn gen_completions(&mut self, for_shell: Shell, od: OsString) { |
| use std::error::Error; |
| |
| let out_dir = PathBuf::from(od); |
| let name = &*self.meta.bin_name.as_ref().unwrap().clone(); |
| let file_name = match for_shell { |
| Shell::Bash => format!("{}.bash", name), |
| Shell::Fish => format!("{}.fish", name), |
| Shell::Zsh => format!("_{}", name), |
| Shell::PowerShell => format!("_{}.ps1", name), |
| Shell::Elvish => format!("{}.elv", name), |
| }; |
| |
| let mut file = match File::create(out_dir.join(file_name)) { |
| Err(why) => panic!("couldn't create completion file: {}", why.description()), |
| Ok(file) => file, |
| }; |
| self.gen_completions_to(for_shell, &mut file) |
| } |
| |
| #[inline] |
| fn app_debug_asserts(&self) -> bool { |
| assert!(self.verify_positionals()); |
| let should_err = self.groups.iter().all(|g| { |
| g.args.iter().all(|arg| { |
| (self.flags.iter().any(|f| &f.b.name == arg) |
| || self.opts.iter().any(|o| &o.b.name == arg) |
| || self.positionals.values().any(|p| &p.b.name == arg) |
| || self.groups.iter().any(|g| &g.name == arg)) |
| }) |
| }); |
| let g = self.groups.iter().find(|g| { |
| g.args.iter().any(|arg| { |
| !(self.flags.iter().any(|f| &f.b.name == arg) |
| || self.opts.iter().any(|o| &o.b.name == arg) |
| || self.positionals.values().any(|p| &p.b.name == arg) |
| || self.groups.iter().any(|g| &g.name == arg)) |
| }) |
| }); |
| assert!( |
| should_err, |
| "The group '{}' contains the arg '{}' that doesn't actually exist.", |
| g.unwrap().name, |
| g.unwrap() |
| .args |
| .iter() |
| .find(|arg| !(self.flags.iter().any(|f| &&f.b.name == arg) |
| || self.opts.iter().any(|o| &&o.b.name == arg) |
| || self.positionals.values().any(|p| &&p.b.name == arg) |
| || self.groups.iter().any(|g| &&g.name == arg))) |
| .unwrap() |
| ); |
| true |
| } |
| |
| #[inline] |
| fn debug_asserts(&self, a: &Arg) -> bool { |
| assert!( |
| !arg_names!(self).any(|name| name == a.b.name), |
| format!("Non-unique argument name: {} is already in use", a.b.name) |
| ); |
| if let Some(l) = a.s.long { |
| assert!( |
| !self.contains_long(l), |
| "Argument long must be unique\n\n\t--{} is already in use", |
| l |
| ); |
| } |
| if let Some(s) = a.s.short { |
| assert!( |
| !self.contains_short(s), |
| "Argument short must be unique\n\n\t-{} is already in use", |
| s |
| ); |
| } |
| let i = if a.index.is_none() { |
| (self.positionals.len() + 1) |
| } else { |
| a.index.unwrap() as usize |
| }; |
| assert!( |
| !self.positionals.contains_key(i), |
| "Argument \"{}\" has the same index as another positional \ |
| argument\n\n\tPerhaps try .multiple(true) to allow one positional argument \ |
| to take multiple values", |
| a.b.name |
| ); |
| assert!( |
| !(a.is_set(ArgSettings::Required) && a.is_set(ArgSettings::Global)), |
| "Global arguments cannot be required.\n\n\t'{}' is marked as \ |
| global and required", |
| a.b.name |
| ); |
| if a.b.is_set(ArgSettings::Last) { |
| assert!( |
| !self |
| .positionals |
| .values() |
| .any(|p| p.b.is_set(ArgSettings::Last)), |
| "Only one positional argument may have last(true) set. Found two." |
| ); |
| assert!(a.s.long.is_none(), |
| "Flags or Options may not have last(true) set. {} has both a long and last(true) set.", |
| a.b.name); |
| assert!(a.s.short.is_none(), |
| "Flags or Options may not have last(true) set. {} has both a short and last(true) set.", |
| a.b.name); |
| } |
| true |
| } |
| |
| #[inline] |
| fn add_conditional_reqs(&mut self, a: &Arg<'a, 'b>) { |
| if let Some(ref r_ifs) = a.r_ifs { |
| for &(arg, val) in r_ifs { |
| self.r_ifs.push((arg, val, a.b.name)); |
| } |
| } |
| } |
| |
| #[inline] |
| fn add_arg_groups(&mut self, a: &Arg<'a, 'b>) { |
| if let Some(ref grps) = a.b.groups { |
| for g in grps { |
| let mut found = false; |
| if let Some(ref mut ag) = self.groups.iter_mut().find(|grp| &grp.name == g) { |
| ag.args.push(a.b.name); |
| found = true; |
| } |
| if !found { |
| let mut ag = ArgGroup::with_name(g); |
| ag.args.push(a.b.name); |
| self.groups.push(ag); |
| } |
| } |
| } |
| } |
| |
| #[inline] |
| fn add_reqs(&mut self, a: &Arg<'a, 'b>) { |
| if a.is_set(ArgSettings::Required) { |
| // If the arg is required, add all it's requirements to master required list |
| self.required.push(a.b.name); |
| if let Some(ref areqs) = a.b.requires { |
| for name in areqs |
| .iter() |
| .filter(|&&(val, _)| val.is_none()) |
| .map(|&(_, name)| name) |
| { |
| self.required.push(name); |
| } |
| } |
| } |
| } |
| |
| #[inline] |
| fn implied_settings(&mut self, a: &Arg<'a, 'b>) { |
| if a.is_set(ArgSettings::Last) { |
| // if an arg has `Last` set, we need to imply DontCollapseArgsInUsage so that args |
| // in the usage string don't get confused or left out. |
| self.set(AS::DontCollapseArgsInUsage); |
| self.set(AS::ContainsLast); |
| } |
| if let Some(l) = a.s.long { |
| if l == "version" { |
| self.unset(AS::NeedsLongVersion); |
| } else if l == "help" { |
| self.unset(AS::NeedsLongHelp); |
| } |
| } |
| } |
| |
| // actually adds the arguments |
| pub fn add_arg(&mut self, a: Arg<'a, 'b>) { |
| // if it's global we have to clone anyways |
| if a.is_set(ArgSettings::Global) { |
| return self.add_arg_ref(&a); |
| } |
| debug_assert!(self.debug_asserts(&a)); |
| self.add_conditional_reqs(&a); |
| self.add_arg_groups(&a); |
| self.add_reqs(&a); |
| self.implied_settings(&a); |
| if a.index.is_some() || (a.s.short.is_none() && a.s.long.is_none()) { |
| let i = if a.index.is_none() { |
| (self.positionals.len() + 1) |
| } else { |
| a.index.unwrap() as usize |
| }; |
| self.positionals |
| .insert(i, PosBuilder::from_arg(a, i as u64)); |
| } else if a.is_set(ArgSettings::TakesValue) { |
| let mut ob = OptBuilder::from(a); |
| ob.s.unified_ord = self.flags.len() + self.opts.len(); |
| self.opts.push(ob); |
| } else { |
| let mut fb = FlagBuilder::from(a); |
| fb.s.unified_ord = self.flags.len() + self.opts.len(); |
| self.flags.push(fb); |
| } |
| } |
| // actually adds the arguments but from a borrow (which means we have to do some cloning) |
| pub fn add_arg_ref(&mut self, a: &Arg<'a, 'b>) { |
| debug_assert!(self.debug_asserts(a)); |
| self.add_conditional_reqs(a); |
| self.add_arg_groups(a); |
| self.add_reqs(a); |
| self.implied_settings(a); |
| if a.index.is_some() || (a.s.short.is_none() && a.s.long.is_none()) { |
| let i = if a.index.is_none() { |
| (self.positionals.len() + 1) |
| } else { |
| a.index.unwrap() as usize |
| }; |
| let pb = PosBuilder::from_arg_ref(a, i as u64); |
| self.positionals.insert(i, pb); |
| } else if a.is_set(ArgSettings::TakesValue) { |
| let mut ob = OptBuilder::from(a); |
| ob.s.unified_ord = self.flags.len() + self.opts.len(); |
| self.opts.push(ob); |
| } else { |
| let mut fb = FlagBuilder::from(a); |
| fb.s.unified_ord = self.flags.len() + self.opts.len(); |
| self.flags.push(fb); |
| } |
| if a.is_set(ArgSettings::Global) { |
| self.global_args.push(a.into()); |
| } |
| } |
| |
| pub fn add_group(&mut self, group: ArgGroup<'a>) { |
| if group.required { |
| self.required.push(group.name); |
| if let Some(ref reqs) = group.requires { |
| self.required.extend_from_slice(reqs); |
| } |
| // if let Some(ref bl) = group.conflicts { |
| // self.blacklist.extend_from_slice(bl); |
| // } |
| } |
| if self.groups.iter().any(|g| g.name == group.name) { |
| let grp = self |
| .groups |
| .iter_mut() |
| .find(|g| g.name == group.name) |
| .expect(INTERNAL_ERROR_MSG); |
| grp.args.extend_from_slice(&group.args); |
| grp.requires = group.requires.clone(); |
| grp.conflicts = group.conflicts.clone(); |
| grp.required = group.required; |
| } else { |
| self.groups.push(group); |
| } |
| } |
| |
| pub fn add_subcommand(&mut self, mut subcmd: App<'a, 'b>) { |
| debugln!( |
| "Parser::add_subcommand: term_w={:?}, name={}", |
| self.meta.term_w, |
| subcmd.p.meta.name |
| ); |
| subcmd.p.meta.term_w = self.meta.term_w; |
| if subcmd.p.meta.name == "help" { |
| self.unset(AS::NeedsSubcommandHelp); |
| } |
| |
| self.subcommands.push(subcmd); |
| } |
| |
| pub fn propagate_settings(&mut self) { |
| debugln!( |
| "Parser::propagate_settings: self={}, g_settings={:#?}", |
| self.meta.name, |
| self.g_settings |
| ); |
| for sc in &mut self.subcommands { |
| debugln!( |
| "Parser::propagate_settings: sc={}, settings={:#?}, g_settings={:#?}", |
| sc.p.meta.name, |
| sc.p.settings, |
| sc.p.g_settings |
| ); |
| // We have to create a new scope in order to tell rustc the borrow of `sc` is |
| // done and to recursively call this method |
| { |
| let vsc = self.settings.is_set(AS::VersionlessSubcommands); |
| let gv = self.settings.is_set(AS::GlobalVersion); |
| |
| if vsc { |
| sc.p.set(AS::DisableVersion); |
| } |
| if gv && sc.p.meta.version.is_none() && self.meta.version.is_some() { |
| sc.p.set(AS::GlobalVersion); |
| sc.p.meta.version = Some(self.meta.version.unwrap()); |
| } |
| sc.p.settings = sc.p.settings | self.g_settings; |
| sc.p.g_settings = sc.p.g_settings | self.g_settings; |
| sc.p.meta.term_w = self.meta.term_w; |
| sc.p.meta.max_w = self.meta.max_w; |
| } |
| sc.p.propagate_settings(); |
| } |
| } |
| |
| #[cfg_attr(feature = "lints", allow(needless_borrow))] |
| pub fn derive_display_order(&mut self) { |
| if self.is_set(AS::DeriveDisplayOrder) { |
| let unified = self.is_set(AS::UnifiedHelpMessage); |
| for (i, o) in self |
| .opts |
| .iter_mut() |
| .enumerate() |
| .filter(|&(_, ref o)| o.s.disp_ord == 999) |
| { |
| o.s.disp_ord = if unified { o.s.unified_ord } else { i }; |
| } |
| for (i, f) in self |
| .flags |
| .iter_mut() |
| .enumerate() |
| .filter(|&(_, ref f)| f.s.disp_ord == 999) |
| { |
| f.s.disp_ord = if unified { f.s.unified_ord } else { i }; |
| } |
| for (i, sc) in &mut self |
| .subcommands |
| .iter_mut() |
| .enumerate() |
| .filter(|&(_, ref sc)| sc.p.meta.disp_ord == 999) |
| { |
| sc.p.meta.disp_ord = i; |
| } |
| } |
| for sc in &mut self.subcommands { |
| sc.p.derive_display_order(); |
| } |
| } |
| |
| pub fn required(&self) -> Iter<&str> { |
| self.required.iter() |
| } |
| |
| #[cfg_attr(feature = "lints", allow(needless_borrow))] |
| #[inline] |
| pub fn has_args(&self) -> bool { |
| !(self.flags.is_empty() && self.opts.is_empty() && self.positionals.is_empty()) |
| } |
| |
| #[inline] |
| pub fn has_opts(&self) -> bool { |
| !self.opts.is_empty() |
| } |
| |
| #[inline] |
| pub fn has_flags(&self) -> bool { |
| !self.flags.is_empty() |
| } |
| |
| #[inline] |
| pub fn has_positionals(&self) -> bool { |
| !self.positionals.is_empty() |
| } |
| |
| #[inline] |
| pub fn has_subcommands(&self) -> bool { |
| !self.subcommands.is_empty() |
| } |
| |
| #[inline] |
| pub fn has_visible_opts(&self) -> bool { |
| if self.opts.is_empty() { |
| return false; |
| } |
| self.opts.iter().any(|o| !o.is_set(ArgSettings::Hidden)) |
| } |
| |
| #[inline] |
| pub fn has_visible_flags(&self) -> bool { |
| if self.flags.is_empty() { |
| return false; |
| } |
| self.flags.iter().any(|f| !f.is_set(ArgSettings::Hidden)) |
| } |
| |
| #[inline] |
| pub fn has_visible_positionals(&self) -> bool { |
| if self.positionals.is_empty() { |
| return false; |
| } |
| self.positionals |
| .values() |
| .any(|p| !p.is_set(ArgSettings::Hidden)) |
| } |
| |
| #[inline] |
| pub fn has_visible_subcommands(&self) -> bool { |
| self.has_subcommands() |
| && self |
| .subcommands |
| .iter() |
| .filter(|sc| sc.p.meta.name != "help") |
| .any(|sc| !sc.p.is_set(AS::Hidden)) |
| } |
| |
| #[inline] |
| pub fn is_set(&self, s: AS) -> bool { |
| self.settings.is_set(s) |
| } |
| |
| #[inline] |
| pub fn set(&mut self, s: AS) { |
| self.settings.set(s) |
| } |
| |
| #[inline] |
| pub fn unset(&mut self, s: AS) { |
| self.settings.unset(s) |
| } |
| |
| #[cfg_attr(feature = "lints", allow(block_in_if_condition_stmt))] |
| pub fn verify_positionals(&self) -> bool { |
| // Because you must wait until all arguments have been supplied, this is the first chance |
| // to make assertions on positional argument indexes |
| // |
| // First we verify that the index highest supplied index, is equal to the number of |
| // positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3 |
| // but no 2) |
| if let Some((idx, p)) = self.positionals.iter().rev().next() { |
| assert!( |
| !(idx != self.positionals.len()), |
| "Found positional argument \"{}\" whose index is {} but there \ |
| are only {} positional arguments defined", |
| p.b.name, |
| idx, |
| self.positionals.len() |
| ); |
| } |
| |
| // Next we verify that only the highest index has a .multiple(true) (if any) |
| if self.positionals.values().any(|a| { |
| a.b.is_set(ArgSettings::Multiple) && (a.index as usize != self.positionals.len()) |
| }) { |
| let mut it = self.positionals.values().rev(); |
| let last = it.next().unwrap(); |
| let second_to_last = it.next().unwrap(); |
| // Either the final positional is required |
| // Or the second to last has a terminator or .last(true) set |
| let ok = last.is_set(ArgSettings::Required) |
| || (second_to_last.v.terminator.is_some() |
| || second_to_last.b.is_set(ArgSettings::Last)) |
| || last.is_set(ArgSettings::Last); |
| assert!( |
| ok, |
| "When using a positional argument with .multiple(true) that is *not the \ |
| last* positional argument, the last positional argument (i.e the one \ |
| with the highest index) *must* have .required(true) or .last(true) set." |
| ); |
| let ok = second_to_last.is_set(ArgSettings::Multiple) || last.is_set(ArgSettings::Last); |
| assert!( |
| ok, |
| "Only the last positional argument, or second to last positional \ |
| argument may be set to .multiple(true)" |
| ); |
| |
| let count = self |
| .positionals |
| .values() |
| .filter(|p| p.b.settings.is_set(ArgSettings::Multiple) && p.v.num_vals.is_none()) |
| .count(); |
| let ok = count <= 1 |
| || (last.is_set(ArgSettings::Last) |
| && last.is_set(ArgSettings::Multiple) |
| && second_to_last.is_set(ArgSettings::Multiple) |
| && count == 2); |
| assert!( |
| ok, |
| "Only one positional argument with .multiple(true) set is allowed per \ |
| command, unless the second one also has .last(true) set" |
| ); |
| } |
| |
| if self.is_set(AS::AllowMissingPositional) { |
| // Check that if a required positional argument is found, all positions with a lower |
| // index are also required. |
| let mut found = false; |
| let mut foundx2 = false; |
| for p in self.positionals.values().rev() { |
| if foundx2 && !p.b.settings.is_set(ArgSettings::Required) { |
| assert!( |
| p.b.is_set(ArgSettings::Required), |
| "Found positional argument which is not required with a lower \ |
| index than a required positional argument by two or more: {:?} \ |
| index {}", |
| p.b.name, |
| p.index |
| ); |
| } else if p.b.is_set(ArgSettings::Required) && !p.b.is_set(ArgSettings::Last) { |
| // Args that .last(true) don't count since they can be required and have |
| // positionals with a lower index that aren't required |
| // Imagine: prog <req1> [opt1] -- <req2> |
| // Both of these are valid invocations: |
| // $ prog r1 -- r2 |
| // $ prog r1 o1 -- r2 |
| if found { |
| foundx2 = true; |
| continue; |
| } |
| found = true; |
| continue; |
| } else { |
| found = false; |
| } |
| } |
| } else { |
| // Check that if a required positional argument is found, all positions with a lower |
| // index are also required |
| let mut found = false; |
| for p in self.positionals.values().rev() { |
| if found { |
| assert!( |
| p.b.is_set(ArgSettings::Required), |
| "Found positional argument which is not required with a lower \ |
| index than a required positional argument: {:?} index {}", |
| p.b.name, |
| p.index |
| ); |
| } else if p.b.is_set(ArgSettings::Required) && !p.b.is_set(ArgSettings::Last) { |
| // Args that .last(true) don't count since they can be required and have |
| // positionals with a lower index that aren't required |
| // Imagine: prog <req1> [opt1] -- <req2> |
| // Both of these are valid invocations: |
| // $ prog r1 -- r2 |
| // $ prog r1 o1 -- r2 |
| found = true; |
| continue; |
| } |
| } |
| } |
| if self |
| .positionals |
| .values() |
| .any(|p| p.b.is_set(ArgSettings::Last) && p.b.is_set(ArgSettings::Required)) |
| && self.has_subcommands() |
| && !self.is_set(AS::SubcommandsNegateReqs) |
| { |
| panic!( |
| "Having a required positional argument with .last(true) set *and* child \ |
| subcommands without setting SubcommandsNegateReqs isn't compatible." |
| ); |
| } |
| |
| true |
| } |
| |
| pub fn propagate_globals(&mut self) { |
| for sc in &mut self.subcommands { |
| // We have to create a new scope in order to tell rustc the borrow of `sc` is |
| // done and to recursively call this method |
| { |
| for a in &self.global_args { |
| sc.p.add_arg_ref(a); |
| } |
| } |
| sc.p.propagate_globals(); |
| } |
| } |
| |
| // Checks if the arg matches a subcommand name, or any of it's aliases (if defined) |
| fn possible_subcommand(&self, arg_os: &OsStr) -> (bool, Option<&str>) { |
| #[cfg(any(target_os = "windows", target_arch = "wasm32"))] |
| use osstringext::OsStrExt3; |
| #[cfg(not(any(target_os = "windows", target_arch = "wasm32")))] |
| use std::os::unix::ffi::OsStrExt; |
| debugln!("Parser::possible_subcommand: arg={:?}", arg_os); |
| fn starts(h: &str, n: &OsStr) -> bool { |
| let n_bytes = n.as_bytes(); |
| let h_bytes = OsStr::new(h).as_bytes(); |
| |
| h_bytes.starts_with(n_bytes) |
| } |
| |
| if self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound) { |
| return (false, None); |
| } |
| if !self.is_set(AS::InferSubcommands) { |
| if let Some(sc) = find_subcmd!(self, arg_os) { |
| return (true, Some(&sc.p.meta.name)); |
| } |
| } else { |
| let v = self |
| .subcommands |
| .iter() |
| .filter(|s| { |
| starts(&s.p.meta.name[..], &*arg_os) |
| || (s.p.meta.aliases.is_some() |
| && s.p |
| .meta |
| .aliases |
| .as_ref() |
| .unwrap() |
| .iter() |
| .filter(|&&(a, _)| starts(a, &*arg_os)) |
| .count() |
| == 1) |
| }) |
| .map(|sc| &sc.p.meta.name) |
| .collect::<Vec<_>>(); |
| |
| for sc in &v { |
| if OsStr::new(sc) == arg_os { |
| return (true, Some(sc)); |
| } |
| } |
| |
| if v.len() == 1 { |
| return (true, Some(v[0])); |
| } |
| } |
| (false, None) |
| } |
| |
| fn parse_help_subcommand<I, T>(&self, it: &mut I) -> ClapResult<ParseResult<'a>> |
| where |
| I: Iterator<Item = T>, |
| T: Into<OsString>, |
| { |
| debugln!("Parser::parse_help_subcommand;"); |
| let cmds: Vec<OsString> = it.map(|c| c.into()).collect(); |
| let mut help_help = false; |
| let mut bin_name = self |
| .meta |
| .bin_name |
| .as_ref() |
| .unwrap_or(&self.meta.name) |
| .clone(); |
| let mut sc = { |
| let mut sc: &Parser = self; |
| for (i, cmd) in cmds.iter().enumerate() { |
| if &*cmd.to_string_lossy() == "help" { |
| // cmd help help |
| help_help = true; |
| } |
| if let Some(c) = sc |
| .subcommands |
| .iter() |
| .find(|s| &*s.p.meta.name == cmd) |
| .map(|sc| &sc.p) |
| { |
| sc = c; |
| if i == cmds.len() - 1 { |
| break; |
| } |
| } else if let Some(c) = sc |
| .subcommands |
| .iter() |
| .find(|s| { |
| if let Some(ref als) = s.p.meta.aliases { |
| als.iter().any(|&(a, _)| a == &*cmd.to_string_lossy()) |
| } else { |
| false |
| } |
| }) |
| .map(|sc| &sc.p) |
| { |
| sc = c; |
| if i == cmds.len() - 1 { |
| break; |
| } |
| } else { |
| return Err(Error::unrecognized_subcommand( |
| cmd.to_string_lossy().into_owned(), |
| self.meta.bin_name.as_ref().unwrap_or(&self.meta.name), |
| self.color(), |
| )); |
| } |
| bin_name = format!("{} {}", bin_name, &*sc.meta.name); |
| } |
| sc.clone() |
| }; |
| if help_help { |
| let mut pb = PosBuilder::new("subcommand", 1); |
| pb.b.help = Some("The subcommand whose help message to display"); |
| pb.set(ArgSettings::Multiple); |
| sc.positionals.insert(1, pb); |
| sc.settings = sc.settings | self.g_settings; |
| } else { |
| sc.create_help_and_version(); |
| } |
| if sc.meta.bin_name != self.meta.bin_name { |
| sc.meta.bin_name = Some(format!("{} {}", bin_name, sc.meta.name)); |
| } |
| Err(sc._help(false)) |
| } |
| |
| // allow wrong self convention due to self.valid_neg_num = true and it's a private method |
| #[cfg_attr(feature = "lints", allow(wrong_self_convention))] |
| fn is_new_arg(&mut self, arg_os: &OsStr, needs_val_of: ParseResult) -> bool { |
| debugln!("Parser::is_new_arg:{:?}:{:?}", arg_os, needs_val_of); |
| let app_wide_settings = if self.is_set(AS::AllowLeadingHyphen) { |
| true |
| } else if self.is_set(AS::AllowNegativeNumbers) { |
| let a = arg_os.to_string_lossy(); |
| if a.parse::<i64>().is_ok() || a.parse::<f64>().is_ok() { |
| self.set(AS::ValidNegNumFound); |
| true |
| } else { |
| false |
| } |
| } else { |
| false |
| }; |
| let arg_allows_tac = match needs_val_of { |
| ParseResult::Opt(name) => { |
| let o = self |
| .opts |
| .iter() |
| .find(|o| o.b.name == name) |
| .expect(INTERNAL_ERROR_MSG); |
| (o.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings) |
| } |
| ParseResult::Pos(name) => { |
| let p = self |
| .positionals |
| .values() |
| .find(|p| p.b.name == name) |
| .expect(INTERNAL_ERROR_MSG); |
| (p.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings) |
| } |
| ParseResult::ValuesDone => return true, |
| _ => false, |
| }; |
| debugln!("Parser::is_new_arg: arg_allows_tac={:?}", arg_allows_tac); |
| |
| // Is this a new argument, or values from a previous option? |
| let mut ret = if arg_os.starts_with(b"--") { |
| debugln!("Parser::is_new_arg: -- found"); |
| if arg_os.len() == 2 && !arg_allows_tac { |
| return true; // We have to return true so override everything else |
| } else if arg_allows_tac { |
| return false; |
| } |
| true |
| } else if arg_os.starts_with(b"-") { |
| debugln!("Parser::is_new_arg: - found"); |
| // a singe '-' by itself is a value and typically means "stdin" on unix systems |
| !(arg_os.len() == 1) |
| } else { |
| debugln!("Parser::is_new_arg: probably value"); |
| false |
| }; |
| |
| ret = ret && !arg_allows_tac; |
| |
| debugln!("Parser::is_new_arg: starts_new_arg={:?}", ret); |
| ret |
| } |
| |
| // The actual parsing function |
| #[cfg_attr(feature = "lints", allow(while_let_on_iterator, collapsible_if))] |
| pub fn get_matches_with<I, T>( |
| &mut self, |
| matcher: &mut ArgMatcher<'a>, |
| it: &mut Peekable<I>, |
| ) -> ClapResult<()> |
| where |
| I: Iterator<Item = T>, |
| T: Into<OsString> + Clone, |
| { |
| debugln!("Parser::get_matches_with;"); |
| // Verify all positional assertions pass |
| debug_assert!(self.app_debug_asserts()); |
| if self.positionals.values().any(|a| { |
| a.b.is_set(ArgSettings::Multiple) && (a.index as usize != self.positionals.len()) |
| }) && self |
| .positionals |
| .values() |
| .last() |
| .map_or(false, |p| !p.is_set(ArgSettings::Last)) |
| { |
| self.settings.set(AS::LowIndexMultiplePositional); |
| } |
| let has_args = self.has_args(); |
| |
| // Next we create the `--help` and `--version` arguments and add them if |
| // necessary |
| self.create_help_and_version(); |
| |
| let mut subcmd_name: Option<String> = None; |
| let mut needs_val_of: ParseResult<'a> = ParseResult::NotFound; |
| let mut pos_counter = 1; |
| let mut sc_is_external = false; |
| while let Some(arg) = it.next() { |
| let arg_os = arg.into(); |
| debugln!( |
| "Parser::get_matches_with: Begin parsing '{:?}' ({:?})", |
| arg_os, |
| &*arg_os.as_bytes() |
| ); |
| |
| self.unset(AS::ValidNegNumFound); |
| // Is this a new argument, or values from a previous option? |
| let starts_new_arg = self.is_new_arg(&arg_os, needs_val_of); |
| if !self.is_set(AS::TrailingValues) |
| && arg_os.starts_with(b"--") |
| && arg_os.len() == 2 |
| && starts_new_arg |
| { |
| debugln!("Parser::get_matches_with: setting TrailingVals=true"); |
| self.set(AS::TrailingValues); |
| continue; |
| } |
| |
| // Has the user already passed '--'? Meaning only positional args follow |
| if !self.is_set(AS::TrailingValues) { |
| // Does the arg match a subcommand name, or any of it's aliases (if defined) |
| { |
| match needs_val_of { |
| ParseResult::Opt(_) | ParseResult::Pos(_) => (), |
| _ => { |
| let (is_match, sc_name) = self.possible_subcommand(&arg_os); |
| debugln!( |
| "Parser::get_matches_with: possible_sc={:?}, sc={:?}", |
| is_match, |
| sc_name |
| ); |
| if is_match { |
| let sc_name = sc_name.expect(INTERNAL_ERROR_MSG); |
| if sc_name == "help" && self.is_set(AS::NeedsSubcommandHelp) { |
| self.parse_help_subcommand(it)?; |
| } |
| subcmd_name = Some(sc_name.to_owned()); |
| break; |
| } |
| } |
| } |
| } |
| |
| if starts_new_arg { |
| let check_all = self.is_set(AS::AllArgsOverrideSelf); |
| { |
| let any_arg = find_any_by_name!(self, self.cache.unwrap_or("")); |
| matcher.process_arg_overrides( |
| any_arg, |
| &mut self.overrides, |
| &mut self.required, |
| check_all, |
| ); |
| } |
| |
| if arg_os.starts_with(b"--") { |
| needs_val_of = self.parse_long_arg(matcher, &arg_os, it)?; |
| debugln!( |
| "Parser:get_matches_with: After parse_long_arg {:?}", |
| needs_val_of |
| ); |
| match needs_val_of { |
| ParseResult::Flag | ParseResult::Opt(..) | ParseResult::ValuesDone => { |
| continue |
| } |
| _ => (), |
| } |
| } else if arg_os.starts_with(b"-") && arg_os.len() != 1 { |
| // Try to parse short args like normal, if AllowLeadingHyphen or |
| // AllowNegativeNumbers is set, parse_short_arg will *not* throw |
| // an error, and instead return Ok(None) |
| needs_val_of = self.parse_short_arg(matcher, &arg_os)?; |
| // If it's None, we then check if one of those two AppSettings was set |
| debugln!( |
| "Parser:get_matches_with: After parse_short_arg {:?}", |
| needs_val_of |
| ); |
| match needs_val_of { |
| ParseResult::MaybeNegNum => { |
| if !(arg_os.to_string_lossy().parse::<i64>().is_ok() |
| || arg_os.to_string_lossy().parse::<f64>().is_ok()) |
| { |
| return Err(Error::unknown_argument( |
| &*arg_os.to_string_lossy(), |
| "", |
| &*usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )); |
| } |
| } |
| ParseResult::Opt(..) | ParseResult::Flag | ParseResult::ValuesDone => { |
| continue |
| } |
| _ => (), |
| } |
| } |
| } else { |
| if let ParseResult::Opt(name) = needs_val_of { |
| // Check to see if parsing a value from a previous arg |
| let arg = self |
| .opts |
| .iter() |
| .find(|o| o.b.name == name) |
| .expect(INTERNAL_ERROR_MSG); |
| // get the OptBuilder so we can check the settings |
| needs_val_of = self.add_val_to_arg(arg, &arg_os, matcher)?; |
| // get the next value from the iterator |
| continue; |
| } |
| } |
| } |
| |
| if !(self.is_set(AS::ArgsNegateSubcommands) && self.is_set(AS::ValidArgFound)) |
| && !self.is_set(AS::InferSubcommands) |
| && !self.is_set(AS::AllowExternalSubcommands) |
| { |
| if let Some(cdate) = |
| suggestions::did_you_mean(&*arg_os.to_string_lossy(), sc_names!(self)) |
| { |
| return Err(Error::invalid_subcommand( |
| arg_os.to_string_lossy().into_owned(), |
| cdate, |
| self.meta.bin_name.as_ref().unwrap_or(&self.meta.name), |
| &*usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )); |
| } |
| } |
| |
| let low_index_mults = self.is_set(AS::LowIndexMultiplePositional) |
| && pos_counter == (self.positionals.len() - 1); |
| let missing_pos = self.is_set(AS::AllowMissingPositional) |
| && (pos_counter == (self.positionals.len() - 1) |
| && !self.is_set(AS::TrailingValues)); |
| debugln!( |
| "Parser::get_matches_with: Positional counter...{}", |
| pos_counter |
| ); |
| debugln!( |
| "Parser::get_matches_with: Low index multiples...{:?}", |
| low_index_mults |
| ); |
| if low_index_mults || missing_pos { |
| if let Some(na) = it.peek() { |
| let n = (*na).clone().into(); |
| needs_val_of = if needs_val_of != ParseResult::ValuesDone { |
| if let Some(p) = self.positionals.get(pos_counter) { |
| ParseResult::Pos(p.b.name) |
| } else { |
| ParseResult::ValuesDone |
| } |
| } else { |
| ParseResult::ValuesDone |
| }; |
| let sc_match = { self.possible_subcommand(&n).0 }; |
| if self.is_new_arg(&n, needs_val_of) |
| || sc_match |
| || suggestions::did_you_mean(&n.to_string_lossy(), sc_names!(self)) |
| .is_some() |
| { |
| debugln!("Parser::get_matches_with: Bumping the positional counter..."); |
| pos_counter += 1; |
| } |
| } else { |
| debugln!("Parser::get_matches_with: Bumping the positional counter..."); |
| pos_counter += 1; |
| } |
| } else if (self.is_set(AS::AllowMissingPositional) && self.is_set(AS::TrailingValues)) |
| || (self.is_set(AS::ContainsLast) && self.is_set(AS::TrailingValues)) |
| { |
| // Came to -- and one postional has .last(true) set, so we go immediately |
| // to the last (highest index) positional |
| debugln!("Parser::get_matches_with: .last(true) and --, setting last pos"); |
| pos_counter = self.positionals.len(); |
| } |
| if let Some(p) = self.positionals.get(pos_counter) { |
| if p.is_set(ArgSettings::Last) && !self.is_set(AS::TrailingValues) { |
| return Err(Error::unknown_argument( |
| &*arg_os.to_string_lossy(), |
| "", |
| &*usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )); |
| } |
| if !self.is_set(AS::TrailingValues) |
| && (self.is_set(AS::TrailingVarArg) && pos_counter == self.positionals.len()) |
| { |
| self.settings.set(AS::TrailingValues); |
| } |
| if self.cache.map_or(true, |name| name != p.b.name) { |
| let check_all = self.is_set(AS::AllArgsOverrideSelf); |
| { |
| let any_arg = find_any_by_name!(self, self.cache.unwrap_or("")); |
| matcher.process_arg_overrides( |
| any_arg, |
| &mut self.overrides, |
| &mut self.required, |
| check_all, |
| ); |
| } |
| self.cache = Some(p.b.name); |
| } |
| let _ = self.add_val_to_arg(p, &arg_os, matcher)?; |
| |
| matcher.inc_occurrence_of(p.b.name); |
| let _ = self |
| .groups_for_arg(p.b.name) |
| .and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); |
| |
| self.settings.set(AS::ValidArgFound); |
| // Only increment the positional counter if it doesn't allow multiples |
| if !p.b.settings.is_set(ArgSettings::Multiple) { |
| pos_counter += 1; |
| } |
| self.settings.set(AS::ValidArgFound); |
| } else if self.is_set(AS::AllowExternalSubcommands) { |
| // Get external subcommand name |
| let sc_name = match arg_os.to_str() { |
| Some(s) => s.to_string(), |
| None => { |
| if !self.is_set(AS::StrictUtf8) { |
| return Err(Error::invalid_utf8( |
| &*usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )); |
| } |
| arg_os.to_string_lossy().into_owned() |
| } |
| }; |
| |
| // Collect the external subcommand args |
| let mut sc_m = ArgMatcher::new(); |
| while let Some(v) = it.next() { |
| let a = v.into(); |
| if a.to_str().is_none() && !self.is_set(AS::StrictUtf8) { |
| return Err(Error::invalid_utf8( |
| &*usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )); |
| } |
| sc_m.add_val_to("", &a); |
| } |
| |
| matcher.subcommand(SubCommand { |
| name: sc_name, |
| matches: sc_m.into(), |
| }); |
| sc_is_external = true; |
| } else if !((self.is_set(AS::AllowLeadingHyphen) |
| || self.is_set(AS::AllowNegativeNumbers)) |
| && arg_os.starts_with(b"-")) |
| && !self.is_set(AS::InferSubcommands) |
| { |
| return Err(Error::unknown_argument( |
| &*arg_os.to_string_lossy(), |
| "", |
| &*usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )); |
| } else if !has_args || self.is_set(AS::InferSubcommands) && self.has_subcommands() { |
| if let Some(cdate) = |
| suggestions::did_you_mean(&*arg_os.to_string_lossy(), sc_names!(self)) |
| { |
| return Err(Error::invalid_subcommand( |
| arg_os.to_string_lossy().into_owned(), |
| cdate, |
| self.meta.bin_name.as_ref().unwrap_or(&self.meta.name), |
| &*usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )); |
| } else { |
| return Err(Error::unrecognized_subcommand( |
| arg_os.to_string_lossy().into_owned(), |
| self.meta.bin_name.as_ref().unwrap_or(&self.meta.name), |
| self.color(), |
| )); |
| } |
| } else { |
| return Err(Error::unknown_argument( |
| &*arg_os.to_string_lossy(), |
| "", |
| &*usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )); |
| } |
| } |
| |
| if !sc_is_external { |
| if let Some(ref pos_sc_name) = subcmd_name { |
| let sc_name = { |
| find_subcmd!(self, pos_sc_name) |
| .expect(INTERNAL_ERROR_MSG) |
| .p |
| .meta |
| .name |
| .clone() |
| }; |
| self.parse_subcommand(&*sc_name, matcher, it)?; |
| } else if self.is_set(AS::SubcommandRequired) { |
| let bn = self.meta.bin_name.as_ref().unwrap_or(&self.meta.name); |
| return Err(Error::missing_subcommand( |
| bn, |
| &usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )); |
| } else if self.is_set(AS::SubcommandRequiredElseHelp) { |
| debugln!("Parser::get_matches_with: SubcommandRequiredElseHelp=true"); |
| let mut out = vec![]; |
| self.write_help_err(&mut out)?; |
| return Err(Error { |
| message: String::from_utf8_lossy(&*out).into_owned(), |
| kind: ErrorKind::MissingArgumentOrSubcommand, |
| info: None, |
| }); |
| } |
| } |
| |
| // In case the last arg was new, we need to process it's overrides |
| let check_all = self.is_set(AS::AllArgsOverrideSelf); |
| { |
| let any_arg = find_any_by_name!(self, self.cache.unwrap_or("")); |
| matcher.process_arg_overrides( |
| any_arg, |
| &mut self.overrides, |
| &mut self.required, |
| check_all, |
| ); |
| } |
| |
| self.remove_overrides(matcher); |
| |
| Validator::new(self).validate(needs_val_of, subcmd_name, matcher) |
| } |
| |
| fn remove_overrides(&mut self, matcher: &mut ArgMatcher) { |
| debugln!("Parser::remove_overrides:{:?};", self.overrides); |
| for &(overr, name) in &self.overrides { |
| debugln!("Parser::remove_overrides:iter:({},{});", overr, name); |
| if matcher.is_present(overr) { |
| debugln!( |
| "Parser::remove_overrides:iter:({},{}): removing {};", |
| overr, |
| name, |
| name |
| ); |
| matcher.remove(name); |
| for i in (0..self.required.len()).rev() { |
| debugln!( |
| "Parser::remove_overrides:iter:({},{}): removing required {};", |
| overr, |
| name, |
| name |
| ); |
| if self.required[i] == name { |
| self.required.swap_remove(i); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| fn propagate_help_version(&mut self) { |
| debugln!("Parser::propagate_help_version;"); |
| self.create_help_and_version(); |
| for sc in &mut self.subcommands { |
| sc.p.propagate_help_version(); |
| } |
| } |
| |
| fn build_bin_names(&mut self) { |
| debugln!("Parser::build_bin_names;"); |
| for sc in &mut self.subcommands { |
| debug!("Parser::build_bin_names:iter: bin_name set..."); |
| if sc.p.meta.bin_name.is_none() { |
| sdebugln!("No"); |
| let bin_name = format!( |
| "{}{}{}", |
| self.meta |
| .bin_name |
| .as_ref() |
| .unwrap_or(&self.meta.name.clone()), |
| if self.meta.bin_name.is_some() { |
| " " |
| } else { |
| "" |
| }, |
| &*sc.p.meta.name |
| ); |
| debugln!( |
| "Parser::build_bin_names:iter: Setting bin_name of {} to {}", |
| self.meta.name, |
| bin_name |
| ); |
| sc.p.meta.bin_name = Some(bin_name); |
| } else { |
| sdebugln!("yes ({:?})", sc.p.meta.bin_name); |
| } |
| debugln!( |
| "Parser::build_bin_names:iter: Calling build_bin_names from...{}", |
| sc.p.meta.name |
| ); |
| sc.p.build_bin_names(); |
| } |
| } |
| |
| fn parse_subcommand<I, T>( |
| &mut self, |
| sc_name: &str, |
| matcher: &mut ArgMatcher<'a>, |
| it: &mut Peekable<I>, |
| ) -> ClapResult<()> |
| where |
| I: Iterator<Item = T>, |
| T: Into<OsString> + Clone, |
| { |
| use std::fmt::Write; |
| debugln!("Parser::parse_subcommand;"); |
| let mut mid_string = String::new(); |
| if !self.is_set(AS::SubcommandsNegateReqs) { |
| let mut hs: Vec<&str> = self.required.iter().map(|n| &**n).collect(); |
| for k in matcher.arg_names() { |
| hs.push(k); |
| } |
| let reqs = usage::get_required_usage_from(self, &hs, Some(matcher), None, false); |
| |
| for s in &reqs { |
| write!(&mut mid_string, " {}", s).expect(INTERNAL_ERROR_MSG); |
| } |
| } |
| mid_string.push_str(" "); |
| if let Some(ref mut sc) = self |
| .subcommands |
| .iter_mut() |
| .find(|s| s.p.meta.name == sc_name) |
| { |
| let mut sc_matcher = ArgMatcher::new(); |
| // bin_name should be parent's bin_name + [<reqs>] + the sc's name separated by |
| // a space |
| sc.p.meta.usage = Some(format!( |
| "{}{}{}", |
| self.meta.bin_name.as_ref().unwrap_or(&String::new()), |
| if self.meta.bin_name.is_some() { |
| &*mid_string |
| } else { |
| "" |
| }, |
| &*sc.p.meta.name |
| )); |
| sc.p.meta.bin_name = Some(format!( |
| "{}{}{}", |
| self.meta.bin_name.as_ref().unwrap_or(&String::new()), |
| if self.meta.bin_name.is_some() { |
| " " |
| } else { |
| "" |
| }, |
| &*sc.p.meta.name |
| )); |
| debugln!( |
| "Parser::parse_subcommand: About to parse sc={}", |
| sc.p.meta.name |
| ); |
| debugln!("Parser::parse_subcommand: sc settings={:#?}", sc.p.settings); |
| sc.p.get_matches_with(&mut sc_matcher, it)?; |
| matcher.subcommand(SubCommand { |
| name: sc.p.meta.name.clone(), |
| matches: sc_matcher.into(), |
| }); |
| } |
| Ok(()) |
| } |
| |
| pub fn groups_for_arg(&self, name: &str) -> Option<Vec<&'a str>> { |
| debugln!("Parser::groups_for_arg: name={}", name); |
| |
| if self.groups.is_empty() { |
| debugln!("Parser::groups_for_arg: No groups defined"); |
| return None; |
| } |
| let mut res = vec![]; |
| debugln!("Parser::groups_for_arg: Searching through groups..."); |
| for grp in &self.groups { |
| for a in &grp.args { |
| if a == &name { |
| sdebugln!("\tFound '{}'", grp.name); |
| res.push(&*grp.name); |
| } |
| } |
| } |
| if res.is_empty() { |
| return None; |
| } |
| |
| Some(res) |
| } |
| |
| pub fn args_in_group(&self, group: &str) -> Vec<String> { |
| debug_assert!(self.app_debug_asserts()); |
| |
| let mut g_vec = vec![]; |
| let mut args = vec![]; |
| |
| for n in &self |
| .groups |
| .iter() |
| .find(|g| g.name == group) |
| .expect(INTERNAL_ERROR_MSG) |
| .args |
| { |
| if let Some(f) = self.flags.iter().find(|f| &f.b.name == n) { |
| args.push(f.to_string()); |
| } else if let Some(f) = self.opts.iter().find(|o| &o.b.name == n) { |
| args.push(f.to_string()); |
| } else if let Some(p) = self.positionals.values().find(|p| &p.b.name == n) { |
| args.push(p.b.name.to_owned()); |
| } else { |
| g_vec.push(*n); |
| } |
| } |
| |
| for av in g_vec.iter().map(|g| self.args_in_group(g)) { |
| args.extend(av); |
| } |
| args.dedup(); |
| args.iter().map(ToOwned::to_owned).collect() |
| } |
| |
| pub fn arg_names_in_group(&self, group: &str) -> Vec<&'a str> { |
| let mut g_vec = vec![]; |
| let mut args = vec![]; |
| |
| for n in &self |
| .groups |
| .iter() |
| .find(|g| g.name == group) |
| .expect(INTERNAL_ERROR_MSG) |
| .args |
| { |
| if self.groups.iter().any(|g| g.name == *n) { |
| args.extend(self.arg_names_in_group(n)); |
| g_vec.push(*n); |
| } else if !args.contains(n) { |
| args.push(*n); |
| } |
| } |
| |
| args.iter().map(|s| *s).collect() |
| } |
| |
| pub fn create_help_and_version(&mut self) { |
| debugln!("Parser::create_help_and_version;"); |
| // name is "hclap_help" because flags are sorted by name |
| if !self.is_set(AS::DisableHelpFlags) && !self.contains_long("help") { |
| debugln!("Parser::create_help_and_version: Building --help"); |
| if self.help_short.is_none() && !self.contains_short('h') { |
| self.help_short = Some('h'); |
| } |
| let arg = FlagBuilder { |
| b: Base { |
| name: "hclap_help", |
| help: self.help_message.or(Some("Prints help information")), |
| ..Default::default() |
| }, |
| s: Switched { |
| short: self.help_short, |
| long: Some("help"), |
| ..Default::default() |
| }, |
| }; |
| self.flags.push(arg); |
| } |
| if !self.is_set(AS::DisableVersion) && !self.contains_long("version") { |
| debugln!("Parser::create_help_and_version: Building --version"); |
| if self.version_short.is_none() && !self.contains_short('V') { |
| self.version_short = Some('V'); |
| } |
| // name is "vclap_version" because flags are sorted by name |
| let arg = FlagBuilder { |
| b: Base { |
| name: "vclap_version", |
| help: self.version_message.or(Some("Prints version information")), |
| ..Default::default() |
| }, |
| s: Switched { |
| short: self.version_short, |
| long: Some("version"), |
| ..Default::default() |
| }, |
| }; |
| self.flags.push(arg); |
| } |
| if !self.subcommands.is_empty() |
| && !self.is_set(AS::DisableHelpSubcommand) |
| && self.is_set(AS::NeedsSubcommandHelp) |
| { |
| debugln!("Parser::create_help_and_version: Building help"); |
| self.subcommands.push( |
| App::new("help") |
| .about("Prints this message or the help of the given subcommand(s)"), |
| ); |
| } |
| } |
| |
| // Retrieves the names of all args the user has supplied thus far, except required ones |
| // because those will be listed in self.required |
| fn check_for_help_and_version_str(&self, arg: &OsStr) -> ClapResult<()> { |
| debugln!("Parser::check_for_help_and_version_str;"); |
| debug!( |
| "Parser::check_for_help_and_version_str: Checking if --{} is help or version...", |
| arg.to_str().unwrap() |
| ); |
| if arg == "help" && self.is_set(AS::NeedsLongHelp) { |
| sdebugln!("Help"); |
| return Err(self._help(true)); |
| } |
| if arg == "version" && self.is_set(AS::NeedsLongVersion) { |
| sdebugln!("Version"); |
| return Err(self._version(true)); |
| } |
| sdebugln!("Neither"); |
| |
| Ok(()) |
| } |
| |
| fn check_for_help_and_version_char(&self, arg: char) -> ClapResult<()> { |
| debugln!("Parser::check_for_help_and_version_char;"); |
| debug!( |
| "Parser::check_for_help_and_version_char: Checking if -{} is help or version...", |
| arg |
| ); |
| if let Some(h) = self.help_short { |
| if arg == h && self.is_set(AS::NeedsLongHelp) { |
| sdebugln!("Help"); |
| return Err(self._help(false)); |
| } |
| } |
| if let Some(v) = self.version_short { |
| if arg == v && self.is_set(AS::NeedsLongVersion) { |
| sdebugln!("Version"); |
| return Err(self._version(false)); |
| } |
| } |
| sdebugln!("Neither"); |
| Ok(()) |
| } |
| |
| fn use_long_help(&self) -> bool { |
| // In this case, both must be checked. This allows the retention of |
| // original formatting, but also ensures that the actual -h or --help |
| // specified by the user is sent through. If HiddenShortHelp is not included, |
| // then items specified with hidden_short_help will also be hidden. |
| let should_long = |v: &Base| { |
| v.long_help.is_some() |
| || v.is_set(ArgSettings::HiddenLongHelp) |
| || v.is_set(ArgSettings::HiddenShortHelp) |
| }; |
| |
| self.meta.long_about.is_some() |
| || self.flags.iter().any(|f| should_long(&f.b)) |
| || self.opts.iter().any(|o| should_long(&o.b)) |
| || self.positionals.values().any(|p| should_long(&p.b)) |
| || self |
| .subcommands |
| .iter() |
| .any(|s| s.p.meta.long_about.is_some()) |
| } |
| |
| fn _help(&self, mut use_long: bool) -> Error { |
| debugln!("Parser::_help: use_long={:?}", use_long); |
| use_long = use_long && self.use_long_help(); |
| let mut buf = vec![]; |
| match Help::write_parser_help(&mut buf, self, use_long) { |
| Err(e) => e, |
| _ => Error { |
| message: String::from_utf8(buf).unwrap_or_default(), |
| kind: ErrorKind::HelpDisplayed, |
| info: None, |
| }, |
| } |
| } |
| |
| fn _version(&self, use_long: bool) -> Error { |
| debugln!("Parser::_version: "); |
| let out = io::stdout(); |
| let mut buf_w = BufWriter::new(out.lock()); |
| match self.print_version(&mut buf_w, use_long) { |
| Err(e) => e, |
| _ => Error { |
| message: String::new(), |
| kind: ErrorKind::VersionDisplayed, |
| info: None, |
| }, |
| } |
| } |
| |
| fn parse_long_arg<I, T>( |
| &mut self, |
| matcher: &mut ArgMatcher<'a>, |
| full_arg: &OsStr, |
| it: &mut Peekable<I>, |
| ) -> ClapResult<ParseResult<'a>> |
| where |
| I: Iterator<Item = T>, |
| T: Into<OsString> + Clone, |
| { |
| // maybe here lifetime should be 'a |
| debugln!("Parser::parse_long_arg;"); |
| |
| // Update the current index |
| self.cur_idx.set(self.cur_idx.get() + 1); |
| |
| let mut val = None; |
| debug!("Parser::parse_long_arg: Does it contain '='..."); |
| let arg = if full_arg.contains_byte(b'=') { |
| let (p0, p1) = full_arg.trim_left_matches(b'-').split_at_byte(b'='); |
| sdebugln!("Yes '{:?}'", p1); |
| val = Some(p1); |
| p0 |
| } else { |
| sdebugln!("No"); |
| full_arg.trim_left_matches(b'-') |
| }; |
| |
| if let Some(opt) = find_opt_by_long!(@os self, arg) { |
| debugln!( |
| "Parser::parse_long_arg: Found valid opt '{}'", |
| opt.to_string() |
| ); |
| self.settings.set(AS::ValidArgFound); |
| let ret = self.parse_opt(val, opt, val.is_some(), matcher)?; |
| if self.cache.map_or(true, |name| name != opt.b.name) { |
| self.cache = Some(opt.b.name); |
| } |
| |
| return Ok(ret); |
| } else if let Some(flag) = find_flag_by_long!(@os self, arg) { |
| debugln!( |
| "Parser::parse_long_arg: Found valid flag '{}'", |
| flag.to_string() |
| ); |
| self.settings.set(AS::ValidArgFound); |
| // Only flags could be help or version, and we need to check the raw long |
| // so this is the first point to check |
| self.check_for_help_and_version_str(arg)?; |
| |
| self.parse_flag(flag, matcher)?; |
| |
| // Handle conflicts, requirements, etc. |
| if self.cache.map_or(true, |name| name != flag.b.name) { |
| self.cache = Some(flag.b.name); |
| } |
| |
| return Ok(ParseResult::Flag); |
| } else if self.is_set(AS::AllowLeadingHyphen) { |
| return Ok(ParseResult::MaybeHyphenValue); |
| } else if self.is_set(AS::ValidNegNumFound) { |
| return Ok(ParseResult::MaybeNegNum); |
| } |
| |
| debugln!("Parser::parse_long_arg: Didn't match anything"); |
| |
| let args_rest: Vec<_> = it.map(|x| x.clone().into()).collect(); |
| let args_rest2: Vec<_> = args_rest |
| .iter() |
| .map(|x| x.to_str().expect(INVALID_UTF8)) |
| .collect(); |
| self.did_you_mean_error(arg.to_str().expect(INVALID_UTF8), matcher, &args_rest2[..]) |
| .map(|_| ParseResult::NotFound) |
| } |
| |
| #[cfg_attr(feature = "lints", allow(len_zero))] |
| fn parse_short_arg( |
| &mut self, |
| matcher: &mut ArgMatcher<'a>, |
| full_arg: &OsStr, |
| ) -> ClapResult<ParseResult<'a>> { |
| debugln!("Parser::parse_short_arg: full_arg={:?}", full_arg); |
| let arg_os = full_arg.trim_left_matches(b'-'); |
| let arg = arg_os.to_string_lossy(); |
| |
| // If AllowLeadingHyphen is set, we want to ensure `-val` gets parsed as `-val` and not |
| // `-v` `-a` `-l` assuming `v` `a` and `l` are all, or mostly, valid shorts. |
| if self.is_set(AS::AllowLeadingHyphen) { |
| if arg.chars().any(|c| !self.contains_short(c)) { |
| debugln!( |
| "Parser::parse_short_arg: LeadingHyphenAllowed yet -{} isn't valid", |
| arg |
| ); |
| return Ok(ParseResult::MaybeHyphenValue); |
| } |
| } else if self.is_set(AS::ValidNegNumFound) { |
| // TODO: Add docs about having AllowNegativeNumbers and `-2` as a valid short |
| // May be better to move this to *after* not finding a valid flag/opt? |
| debugln!("Parser::parse_short_arg: Valid negative num..."); |
| return Ok(ParseResult::MaybeNegNum); |
| } |
| |
| let mut ret = ParseResult::NotFound; |
| for c in arg.chars() { |
| debugln!("Parser::parse_short_arg:iter:{}", c); |
| |
| // update each index because `-abcd` is four indices to clap |
| self.cur_idx.set(self.cur_idx.get() + 1); |
| |
| // Check for matching short options, and return the name if there is no trailing |
| // concatenated value: -oval |
| // Option: -o |
| // Value: val |
| if let Some(opt) = find_opt_by_short!(self, c) { |
| debugln!("Parser::parse_short_arg:iter:{}: Found valid opt", c); |
| self.settings.set(AS::ValidArgFound); |
| // Check for trailing concatenated value |
| let p: Vec<_> = arg.splitn(2, c).collect(); |
| debugln!( |
| "Parser::parse_short_arg:iter:{}: p[0]={:?}, p[1]={:?}", |
| c, |
| p[0].as_bytes(), |
| p[1].as_bytes() |
| ); |
| let i = p[0].as_bytes().len() + 1; |
| let val = if p[1].as_bytes().len() > 0 { |
| debugln!( |
| "Parser::parse_short_arg:iter:{}: val={:?} (bytes), val={:?} (ascii)", |
| c, |
| arg_os.split_at(i).1.as_bytes(), |
| arg_os.split_at(i).1 |
| ); |
| Some(arg_os.split_at(i).1) |
| } else { |
| None |
| }; |
| |
| // Default to "we're expecting a value later" |
| let ret = self.parse_opt(val, opt, false, matcher)?; |
| |
| if self.cache.map_or(true, |name| name != opt.b.name) { |
| self.cache = Some(opt.b.name); |
| } |
| |
| return Ok(ret); |
| } else if let Some(flag) = find_flag_by_short!(self, c) { |
| debugln!("Parser::parse_short_arg:iter:{}: Found valid flag", c); |
| self.settings.set(AS::ValidArgFound); |
| // Only flags can be help or version |
| self.check_for_help_and_version_char(c)?; |
| ret = self.parse_flag(flag, matcher)?; |
| |
| // Handle conflicts, requirements, overrides, etc. |
| // Must be called here due to mutabililty |
| if self.cache.map_or(true, |name| name != flag.b.name) { |
| self.cache = Some(flag.b.name); |
| } |
| } else { |
| let arg = format!("-{}", c); |
| return Err(Error::unknown_argument( |
| &*arg, |
| "", |
| &*usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )); |
| } |
| } |
| Ok(ret) |
| } |
| |
| fn parse_opt( |
| &self, |
| val: Option<&OsStr>, |
| opt: &OptBuilder<'a, 'b>, |
| had_eq: bool, |
| matcher: &mut ArgMatcher<'a>, |
| ) -> ClapResult<ParseResult<'a>> { |
| debugln!("Parser::parse_opt; opt={}, val={:?}", opt.b.name, val); |
| debugln!("Parser::parse_opt; opt.settings={:?}", opt.b.settings); |
| let mut has_eq = false; |
| let no_val = val.is_none(); |
| let empty_vals = opt.is_set(ArgSettings::EmptyValues); |
| let min_vals_zero = opt.v.min_vals.unwrap_or(1) == 0; |
| let needs_eq = opt.is_set(ArgSettings::RequireEquals); |
| |
| debug!("Parser::parse_opt; Checking for val..."); |
| if let Some(fv) = val { |
| has_eq = fv.starts_with(&[b'=']) || had_eq; |
| let v = fv.trim_left_matches(b'='); |
| if !empty_vals && (v.len() == 0 || (needs_eq && !has_eq)) { |
| sdebugln!("Found Empty - Error"); |
| return Err(Error::empty_value( |
| opt, |
| &*usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )); |
| } |
| sdebugln!("Found - {:?}, len: {}", v, v.len()); |
| debugln!( |
| "Parser::parse_opt: {:?} contains '='...{:?}", |
| fv, |
| fv.starts_with(&[b'=']) |
| ); |
| self.add_val_to_arg(opt, v, matcher)?; |
| } else if needs_eq && !(empty_vals || min_vals_zero) { |
| sdebugln!("None, but requires equals...Error"); |
| return Err(Error::empty_value( |
| opt, |
| &*usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )); |
| } else { |
| sdebugln!("None"); |
| } |
| |
| matcher.inc_occurrence_of(opt.b.name); |
| // Increment or create the group "args" |
| self.groups_for_arg(opt.b.name) |
| .and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); |
| |
| let needs_delim = opt.is_set(ArgSettings::RequireDelimiter); |
| let mult = opt.is_set(ArgSettings::Multiple); |
| if no_val && min_vals_zero && !has_eq && needs_eq { |
| debugln!("Parser::parse_opt: More arg vals not required..."); |
| return Ok(ParseResult::ValuesDone); |
| } else if no_val || (mult && !needs_delim) && !has_eq && matcher.needs_more_vals(opt) { |
| debugln!("Parser::parse_opt: More arg vals required..."); |
| return Ok(ParseResult::Opt(opt.b.name)); |
| } |
| debugln!("Parser::parse_opt: More arg vals not required..."); |
| Ok(ParseResult::ValuesDone) |
| } |
| |
| fn add_val_to_arg<A>( |
| &self, |
| arg: &A, |
| val: &OsStr, |
| matcher: &mut ArgMatcher<'a>, |
| ) -> ClapResult<ParseResult<'a>> |
| where |
| A: AnyArg<'a, 'b> + Display, |
| { |
| debugln!("Parser::add_val_to_arg; arg={}, val={:?}", arg.name(), val); |
| debugln!( |
| "Parser::add_val_to_arg; trailing_vals={:?}, DontDelimTrailingVals={:?}", |
| self.is_set(AS::TrailingValues), |
| self.is_set(AS::DontDelimitTrailingValues) |
| ); |
| if !(self.is_set(AS::TrailingValues) && self.is_set(AS::DontDelimitTrailingValues)) { |
| if let Some(delim) = arg.val_delim() { |
| if val.is_empty() { |
| Ok(self.add_single_val_to_arg(arg, val, matcher)?) |
| } else { |
| let mut iret = ParseResult::ValuesDone; |
| for v in val.split(delim as u32 as u8) { |
| iret = self.add_single_val_to_arg(arg, v, matcher)?; |
| } |
| // If there was a delimiter used, we're not looking for more values |
| if val.contains_byte(delim as u32 as u8) |
| || arg.is_set(ArgSettings::RequireDelimiter) |
| { |
| iret = ParseResult::ValuesDone; |
| } |
| Ok(iret) |
| } |
| } else { |
| self.add_single_val_to_arg(arg, val, matcher) |
| } |
| } else { |
| self.add_single_val_to_arg(arg, val, matcher) |
| } |
| } |
| |
| fn add_single_val_to_arg<A>( |
| &self, |
| arg: &A, |
| v: &OsStr, |
| matcher: &mut ArgMatcher<'a>, |
| ) -> ClapResult<ParseResult<'a>> |
| where |
| A: AnyArg<'a, 'b> + Display, |
| { |
| debugln!("Parser::add_single_val_to_arg;"); |
| debugln!("Parser::add_single_val_to_arg: adding val...{:?}", v); |
| |
| // update the current index because each value is a distinct index to clap |
| self.cur_idx.set(self.cur_idx.get() + 1); |
| |
| // @TODO @docs @p4: docs for indices should probably note that a terminator isn't a value |
| // and therefore not reported in indices |
| if let Some(t) = arg.val_terminator() { |
| if t == v { |
| return Ok(ParseResult::ValuesDone); |
| } |
| } |
| |
| matcher.add_val_to(arg.name(), v); |
| matcher.add_index_to(arg.name(), self.cur_idx.get()); |
| |
| // Increment or create the group "args" |
| if let Some(grps) = self.groups_for_arg(arg.name()) { |
| for grp in grps { |
| matcher.add_val_to(&*grp, v); |
| } |
| } |
| |
| if matcher.needs_more_vals(arg) { |
| return Ok(ParseResult::Opt(arg.name())); |
| } |
| Ok(ParseResult::ValuesDone) |
| } |
| |
| fn parse_flag( |
| &self, |
| flag: &FlagBuilder<'a, 'b>, |
| matcher: &mut ArgMatcher<'a>, |
| ) -> ClapResult<ParseResult<'a>> { |
| debugln!("Parser::parse_flag;"); |
| |
| matcher.inc_occurrence_of(flag.b.name); |
| matcher.add_index_to(flag.b.name, self.cur_idx.get()); |
| |
| // Increment or create the group "args" |
| self.groups_for_arg(flag.b.name) |
| .and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); |
| |
| Ok(ParseResult::Flag) |
| } |
| |
| fn did_you_mean_error( |
| &self, |
| arg: &str, |
| matcher: &mut ArgMatcher<'a>, |
| args_rest: &[&str], |
| ) -> ClapResult<()> { |
| // Didn't match a flag or option |
| let suffix = |
| suggestions::did_you_mean_flag_suffix(arg, &args_rest, longs!(self), &self.subcommands); |
| |
| // Add the arg to the matches to build a proper usage string |
| if let Some(name) = suffix.1 { |
| if let Some(opt) = find_opt_by_long!(self, name) { |
| self.groups_for_arg(&*opt.b.name) |
| .and_then(|grps| Some(matcher.inc_occurrences_of(&*grps))); |
| matcher.insert(&*opt.b.name); |
| } else if let Some(flg) = find_flag_by_long!(self, name) { |
| self.groups_for_arg(&*flg.b.name) |
| .and_then(|grps| Some(matcher.inc_occurrences_of(&*grps))); |
| matcher.insert(&*flg.b.name); |
| } |
| } |
| |
| let used_arg = format!("--{}", arg); |
| Err(Error::unknown_argument( |
| &*used_arg, |
| &*suffix.0, |
| &*usage::create_error_usage(self, matcher, None), |
| self.color(), |
| )) |
| } |
| |
| // Prints the version to the user and exits if quit=true |
| fn print_version<W: Write>(&self, w: &mut W, use_long: bool) -> ClapResult<()> { |
| self.write_version(w, use_long)?; |
| w.flush().map_err(Error::from) |
| } |
| |
| pub fn write_version<W: Write>(&self, w: &mut W, use_long: bool) -> io::Result<()> { |
| let ver = if use_long { |
| self.meta |
| .long_version |
| .unwrap_or_else(|| self.meta.version.unwrap_or("")) |
| } else { |
| self.meta |
| .version |
| .unwrap_or_else(|| self.meta.long_version.unwrap_or("")) |
| }; |
| if let Some(bn) = self.meta.bin_name.as_ref() { |
| if bn.contains(' ') { |
| // Incase we're dealing with subcommands i.e. git mv is translated to git-mv |
| write!(w, "{} {}", bn.replace(" ", "-"), ver) |
| } else { |
| write!(w, "{} {}", &self.meta.name[..], ver) |
| } |
| } else { |
| write!(w, "{} {}", &self.meta.name[..], ver) |
| } |
| } |
| |
| pub fn print_help(&self) -> ClapResult<()> { |
| let out = io::stdout(); |
| let mut buf_w = BufWriter::new(out.lock()); |
| self.write_help(&mut buf_w) |
| } |
| |
| pub fn write_help<W: Write>(&self, w: &mut W) -> ClapResult<()> { |
| Help::write_parser_help(w, self, false) |
| } |
| |
| pub fn write_long_help<W: Write>(&self, w: &mut W) -> ClapResult<()> { |
| Help::write_parser_help(w, self, true) |
| } |
| |
| pub fn write_help_err<W: Write>(&self, w: &mut W) -> ClapResult<()> { |
| Help::write_parser_help_to_stderr(w, self) |
| } |
| |
| pub fn add_defaults(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> { |
| debugln!("Parser::add_defaults;"); |
| macro_rules! add_val { |
| (@default $_self:ident, $a:ident, $m:ident) => { |
| if let Some(ref val) = $a.v.default_val { |
| debugln!("Parser::add_defaults:iter:{}: has default vals", $a.b.name); |
| if $m.get($a.b.name).map(|ma| ma.vals.len()).map(|len| len == 0).unwrap_or(false) { |
| debugln!("Parser::add_defaults:iter:{}: has no user defined vals", $a.b.name); |
| $_self.add_val_to_arg($a, OsStr::new(val), $m)?; |
| |
| if $_self.cache.map_or(true, |name| name != $a.name()) { |
| $_self.cache = Some($a.name()); |
| } |
| } else if $m.get($a.b.name).is_some() { |
| debugln!("Parser::add_defaults:iter:{}: has user defined vals", $a.b.name); |
| } else { |
| debugln!("Parser::add_defaults:iter:{}: wasn't used", $a.b.name); |
| |
| $_self.add_val_to_arg($a, OsStr::new(val), $m)?; |
| |
| if $_self.cache.map_or(true, |name| name != $a.name()) { |
| $_self.cache = Some($a.name()); |
| } |
| } |
| } else { |
| debugln!("Parser::add_defaults:iter:{}: doesn't have default vals", $a.b.name); |
| } |
| }; |
| ($_self:ident, $a:ident, $m:ident) => { |
| if let Some(ref vm) = $a.v.default_vals_ifs { |
| sdebugln!(" has conditional defaults"); |
| let mut done = false; |
| if $m.get($a.b.name).is_none() { |
| for &(arg, val, default) in vm.values() { |
| let add = if let Some(a) = $m.get(arg) { |
| if let Some(v) = val { |
| a.vals.iter().any(|value| v == value) |
| } else { |
| true |
| } |
| } else { |
| false |
| }; |
| if add { |
| $_self.add_val_to_arg($a, OsStr::new(default), $m)?; |
| if $_self.cache.map_or(true, |name| name != $a.name()) { |
| $_self.cache = Some($a.name()); |
| } |
| done = true; |
| break; |
| } |
| } |
| } |
| |
| if done { |
| continue; // outer loop (outside macro) |
| } |
| } else { |
| sdebugln!(" doesn't have conditional defaults"); |
| } |
| add_val!(@default $_self, $a, $m) |
| }; |
| } |
| |
| for o in &self.opts { |
| debug!("Parser::add_defaults:iter:{}:", o.b.name); |
| add_val!(self, o, matcher); |
| } |
| for p in self.positionals.values() { |
| debug!("Parser::add_defaults:iter:{}:", p.b.name); |
| add_val!(self, p, matcher); |
| } |
| Ok(()) |
| } |
| |
| pub fn add_env(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> { |
| macro_rules! add_val { |
| ($_self:ident, $a:ident, $m:ident) => { |
| if let Some(ref val) = $a.v.env { |
| if $m |
| .get($a.b.name) |
| .map(|ma| ma.vals.len()) |
| .map(|len| len == 0) |
| .unwrap_or(false) |
| { |
| if let Some(ref val) = val.1 { |
| $_self.add_val_to_arg($a, OsStr::new(val), $m)?; |
| |
| if $_self.cache.map_or(true, |name| name != $a.name()) { |
| $_self.cache = Some($a.name()); |
| } |
| } |
| } else { |
| if let Some(ref val) = val.1 { |
| $_self.add_val_to_arg($a, OsStr::new(val), $m)?; |
| |
| if $_self.cache.map_or(true, |name| name != $a.name()) { |
| $_self.cache = Some($a.name()); |
| } |
| } |
| } |
| } |
| }; |
| } |
| |
| for o in &self.opts { |
| add_val!(self, o, matcher); |
| } |
| for p in self.positionals.values() { |
| add_val!(self, p, matcher); |
| } |
| Ok(()) |
| } |
| |
| pub fn flags(&self) -> Iter<FlagBuilder<'a, 'b>> { |
| self.flags.iter() |
| } |
| |
| pub fn opts(&self) -> Iter<OptBuilder<'a, 'b>> { |
| self.opts.iter() |
| } |
| |
| pub fn positionals(&self) -> map::Values<PosBuilder<'a, 'b>> { |
| self.positionals.values() |
| } |
| |
| pub fn subcommands(&self) -> Iter<App> { |
| self.subcommands.iter() |
| } |
| |
| // Should we color the output? None=determined by output location, true=yes, false=no |
| #[doc(hidden)] |
| pub fn color(&self) -> ColorWhen { |
| debugln!("Parser::color;"); |
| debug!("Parser::color: Color setting..."); |
| if self.is_set(AS::ColorNever) { |
| sdebugln!("Never"); |
| ColorWhen::Never |
| } else if self.is_set(AS::ColorAlways) { |
| sdebugln!("Always"); |
| ColorWhen::Always |
| } else { |
| sdebugln!("Auto"); |
| ColorWhen::Auto |
| } |
| } |
| |
| pub fn find_any_arg(&self, name: &str) -> Option<&AnyArg<'a, 'b>> { |
| if let Some(f) = find_by_name!(self, name, flags, iter) { |
| return Some(f); |
| } |
| if let Some(o) = find_by_name!(self, name, opts, iter) { |
| return Some(o); |
| } |
| if let Some(p) = find_by_name!(self, name, positionals, values) { |
| return Some(p); |
| } |
| None |
| } |
| |
| /// Check is a given string matches the binary name for this parser |
| fn is_bin_name(&self, value: &str) -> bool { |
| self.meta |
| .bin_name |
| .as_ref() |
| .and_then(|name| Some(value == name)) |
| .unwrap_or(false) |
| } |
| |
| /// Check is a given string is an alias for this parser |
| fn is_alias(&self, value: &str) -> bool { |
| self.meta |
| .aliases |
| .as_ref() |
| .and_then(|aliases| { |
| for alias in aliases { |
| if alias.0 == value { |
| return Some(true); |
| } |
| } |
| Some(false) |
| }) |
| .unwrap_or(false) |
| } |
| |
| // Only used for completion scripts due to bin_name messiness |
| #[cfg_attr(feature = "lints", allow(block_in_if_condition_stmt))] |
| pub fn find_subcommand(&'b self, sc: &str) -> Option<&'b App<'a, 'b>> { |
| debugln!("Parser::find_subcommand: sc={}", sc); |
| debugln!( |
| "Parser::find_subcommand: Currently in Parser...{}", |
| self.meta.bin_name.as_ref().unwrap() |
| ); |
| for s in &self.subcommands { |
| if s.p.is_bin_name(sc) { |
| return Some(s); |
| } |
| // XXX: why do we split here? |
| // isn't `sc` supposed to be single word already? |
| let last = sc.split(' ').rev().next().expect(INTERNAL_ERROR_MSG); |
| if s.p.is_alias(last) { |
| return Some(s); |
| } |
| |
| if let Some(app) = s.p.find_subcommand(sc) { |
| return Some(app); |
| } |
| } |
| None |
| } |
| |
| #[inline] |
| fn contains_long(&self, l: &str) -> bool { |
| longs!(self).any(|al| al == &l) |
| } |
| |
| #[inline] |
| fn contains_short(&self, s: char) -> bool { |
| shorts!(self).any(|arg_s| arg_s == &s) |
| } |
| } |