// Std use std::ffi::{OsStr, OsString}; use std::fmt::Display; use std::fs::File; use std::io::{self, BufWriter, Write}; #[cfg(all(feature = "debug", not(any(target_os = "windows", target_arch = "wasm32"))))] use std::os::unix::ffi::OsStrExt; #[cfg(all(feature = "debug", any(target_os = "windows", target_arch = "wasm32")))] use osstringext::OsStrExt3; use std::path::PathBuf; use std::slice::Iter; use std::iter::Peekable; use std::cell::Cell; // Internal use INTERNAL_ERROR_MSG; use INVALID_UTF8; use SubCommand; use app::App; use app::help::Help; use app::meta::AppMeta; use app::settings::AppFlags; use args::{AnyArg, Arg, ArgGroup, ArgMatcher, Base, FlagBuilder, OptBuilder, PosBuilder, Switched}; use args::settings::ArgSettings; use completions::ComplGen; use errors::{Error, ErrorKind}; use errors::Result as ClapResult; use fmt::ColorWhen; use osstringext::OsStrExt2; use completions::Shell; use suggestions; use app::settings::AppSettings as AS; use app::validator::Validator; use app::usage; use map::{self, VecMap}; #[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>, pub opts: Vec>, pub positionals: VecMap>, pub subcommands: Vec>, pub groups: Vec>, pub global_args: Vec>, 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, version_short: Option, cache: Option<&'a str>, pub help_message: Option<&'a str>, pub version_message: Option<&'a str>, cur_idx: Cell, } 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(&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 [opt1] -- // 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 [opt1] -- // 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(not(any(target_os = "windows", target_arch = "wasm32")))] use std::os::unix::ffi::OsStrExt; #[cfg(any(target_os = "windows", target_arch = "wasm32"))] use osstringext::OsStrExt3; 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::>(); 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(&self, it: &mut I) -> ClapResult> where I: Iterator, T: Into, { debugln!("Parser::parse_help_subcommand;"); let cmds: Vec = 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::().is_ok() || a.parse::().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( &mut self, matcher: &mut ArgMatcher<'a>, it: &mut Peekable, ) -> ClapResult<()> where I: Iterator, T: Into + 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 = 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::().is_ok() || arg_os.to_string_lossy().parse::().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( &mut self, sc_name: &str, matcher: &mut ArgMatcher<'a>, it: &mut Peekable, ) -> ClapResult<()> where I: Iterator, T: Into + 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 + [] + 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> { 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 { 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( &mut self, matcher: &mut ArgMatcher<'a>, full_arg: &OsStr, it: &mut Peekable, ) -> ClapResult> where I: Iterator, T: Into + 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> { 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> { 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( &self, arg: &A, val: &OsStr, matcher: &mut ArgMatcher<'a>, ) -> ClapResult> 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( &self, arg: &A, v: &OsStr, matcher: &mut ArgMatcher<'a>, ) -> ClapResult> 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> { 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(&self, w: &mut W, use_long: bool) -> ClapResult<()> { self.write_version(w, use_long)?; w.flush().map_err(Error::from) } pub fn write_version(&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(&self, w: &mut W) -> ClapResult<()> { Help::write_parser_help(w, self, false) } pub fn write_long_help(&self, w: &mut W) -> ClapResult<()> { Help::write_parser_help(w, self, true) } pub fn write_help_err(&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> { self.flags.iter() } pub fn opts(&self) -> Iter> { self.opts.iter() } pub fn positionals(&self) -> map::Values> { self.positionals.values() } pub fn subcommands(&self) -> Iter { 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) } }