// Std use std::collections::{BTreeMap, HashMap, VecDeque}; use std::ffi::{OsStr, OsString}; use std::fmt::Display; use std::fs::File; use std::io::{self, BufWriter, Write}; #[cfg(feature = "debug")] use std::os::unix::ffi::OsStrExt; use std::path::PathBuf; use std::slice::Iter; // Third Party use vec_map::{self, VecMap}; // 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, AppSettings}; use args::{AnyArg, ArgMatcher}; use args::{Arg, ArgGroup, FlagBuilder, OptBuilder, PosBuilder}; use args::MatchedArg; use args::settings::ArgSettings; use completions::ComplGen; use errors::{Error, ErrorKind}; use errors::Result as ClapResult; use fmt::{Format, ColorWhen}; use osstringext::OsStrExt2; use shell::Shell; use suggestions; #[allow(missing_debug_implementations)] #[doc(hidden)] pub struct Parser<'a, 'b> where 'a: 'b { required: Vec<&'b str>, pub short_list: Vec, pub long_list: Vec<&'b str>, blacklist: Vec<&'b str>, // A list of possible flags pub flags: Vec>, // A list of possible options pub opts: Vec>, // A list of positional arguments pub positionals: VecMap>, // A list of subcommands #[doc(hidden)] pub subcommands: Vec>, groups: HashMap<&'a str, ArgGroup<'a>>, global_args: Vec>, overrides: Vec<&'b str>, help_short: Option, version_short: Option, settings: AppFlags, pub g_settings: Vec, pub meta: AppMeta<'b>, trailing_vals: bool, } impl<'a, 'b> Default for Parser<'a, 'b> { fn default() -> Self { Parser { flags: vec![], opts: vec![], positionals: VecMap::new(), subcommands: vec![], help_short: None, version_short: None, required: vec![], short_list: vec![], long_list: vec![], blacklist: vec![], groups: HashMap::new(), global_args: vec![], overrides: vec![], g_settings: vec![], settings: AppFlags::new(), meta: AppMeta::new(), trailing_vals: false, } } } impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { pub fn with_name(n: String) -> Self { Parser { meta: AppMeta::with_name(n), ..Default::default() } } pub fn help_short(&mut self, s: &str) { self.help_short = s.trim_left_matches(|c| c == '-') .chars() .nth(0); } pub fn version_short(&mut self, s: &str) { self.version_short = s.trim_left_matches(|c| c == '-') .chars() .nth(0); } pub fn gen_completions_to(&mut self, for_shell: Shell, buf: &mut W) { self.propogate_help_version(); self.build_bin_names(); 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 suffix = match for_shell { Shell::Bash => ".bash-completion", Shell::Fish => ".fish", }; let mut file = match File::create(out_dir.join(format!("{}{}", &*self.meta.bin_name.as_ref().unwrap(), suffix))) { Err(why) => panic!("couldn't create bash completion file: {}", why.description()), Ok(file) => file, }; self.gen_completions_to(for_shell, &mut file) } // actually adds the arguments pub fn add_arg(&mut self, a: &Arg<'a, 'b>) { debug_assert!(!(self.flags.iter().any(|f| &f.name == &a.name) || self.opts.iter().any(|o| o.name == a.name) || self.positionals.values().any(|p| p.name == a.name)), format!("Non-unique argument name: {} is already in use", a.name)); if let Some(ref grps) = a.group { for g in grps { let ag = self.groups.entry(g).or_insert_with(|| ArgGroup::with_name(g)); ag.args.push(a.name); } } if let Some(s) = a.short { debug_assert!(!self.short_list.contains(&s), format!("Argument short must be unique\n\n\t-{} is already in use", s)); self.short_list.push(s); } if let Some(l) = a.long { debug_assert!(!self.long_list.contains(&l), format!("Argument long must be unique\n\n\t--{} is already in use", l)); self.long_list.push(l); if l == "help" { self.unset(AppSettings::NeedsLongHelp); } else if l == "version" { self.unset(AppSettings::NeedsLongVersion); } } if a.is_set(ArgSettings::Required) { self.required.push(a.name); } if a.index.is_some() || (a.short.is_none() && a.long.is_none()) { let i = if a.index.is_none() { (self.positionals.len() + 1) } else { a.index.unwrap() as usize }; debug_assert!(!self.positionals.contains_key(i), format!("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.name)); let pb = PosBuilder::from_arg(a, i as u64, &mut self.required); self.positionals.insert(i, pb); } else if a.is_set(ArgSettings::TakesValue) { let mut ob = OptBuilder::from_arg(a, &mut self.required); if self.settings.is_set(AppSettings::DeriveDisplayOrder) && a.disp_ord == 999 { ob.disp_ord = if self.settings.is_set(AppSettings::UnifiedHelpMessage) { self.flags.len() + self.opts.len() } else { self.opts.len() }; } self.opts.push(ob); } else { let mut fb = FlagBuilder::from(a); if self.settings.is_set(AppSettings::DeriveDisplayOrder) && a.disp_ord == 999 { fb.disp_ord = if self.settings.is_set(AppSettings::UnifiedHelpMessage) { self.flags.len() + self.opts.len() } else { self.flags.len() }; } self.flags.push(fb); } if a.is_set(ArgSettings::Global) { debug_assert!(!a.is_set(ArgSettings::Required), format!("Global arguments cannot be required.\n\n\t'{}' is marked as \ global and required", a.name)); self.global_args.push(a.into()); } } pub fn add_group(&mut self, group: ArgGroup<'a>) { if group.required { self.required.push(group.name.into()); 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); } } let mut found = false; if let Some(ref mut grp) = self.groups.get_mut(&group.name) { grp.args.extend_from_slice(&group.args); grp.requires = group.requires.clone(); grp.conflicts = group.conflicts.clone(); grp.required = group.required; found = true; } if !found { self.groups.insert(group.name.into(), group); } } pub fn add_subcommand(&mut self, mut subcmd: App<'a, 'b>) { debugln!("fn=Parser::add_subcommand;"); debugln!("Term width...{:?}", self.meta.term_w); subcmd.p.meta.term_w = self.meta.term_w; debug!("Is help..."); if subcmd.p.meta.name == "help" { sdebugln!("Yes"); self.settings.set(AppSettings::NeedsSubcommandHelp); } else { sdebugln!("No"); } if self.settings.is_set(AppSettings::DeriveDisplayOrder) { subcmd.p.meta.disp_ord = self.subcommands.len(); } self.subcommands.push(subcmd); } pub fn propogate_settings(&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 { let vsc = self.settings.is_set(AppSettings::VersionlessSubcommands); let gv = self.settings.is_set(AppSettings::GlobalVersion); debugln!("VersionlessSubcommands set...{:?}", vsc); debugln!("GlobalVersion set...{:?}", gv); if vsc { sc.p.settings.set(AppSettings::DisableVersion); } if gv && sc.p.meta.version.is_none() && self.meta.version.is_some() { sc.p.set(AppSettings::GlobalVersion); sc.p.meta.version = Some(self.meta.version.unwrap()); } for s in &self.g_settings { sc.p.set(*s); sc.p.g_settings.push(*s); } } sc.p.propogate_settings(); } } pub fn required(&self) -> Iter<&str> { self.required.iter() } pub fn get_required_from(&self, reqs: &[&'a str], matcher: Option<&ArgMatcher<'a>>) -> VecDeque { let mut c_flags: Vec<&str> = vec![]; let mut c_pos: Vec<&str> = vec![]; let mut c_opt: Vec<&str> = vec![]; let mut grps: Vec<&str> = vec![]; for name in reqs { if self.flags.iter().any(|f| &f.name == name) { c_flags.push(name); } else if self.opts.iter().any(|o| &o.name == name) { c_opt.push(name); } else if self.groups.contains_key(name) { grps.push(name); } else { c_pos.push(name); } } macro_rules! fill_vecs { ($_self:ident { $t1:ident => $v1:ident => $i1:ident, $t2:ident => $v2:ident => $i2:ident, $t3:ident => $v3:ident => $i3:ident, $gv:ident, $tmp:ident }) => { for a in &$v1 { if let Some(a) = self.$t1.$i1().filter(|arg| &arg.name == a).next() { if let Some(ref rl) = a.requires { for r in rl { if !reqs.contains(r) { if $_self.$t1.$i1().any(|t| &t.name == r) { $tmp.push(*r); } else if $_self.$t2.$i2().any(|t| &t.name == r) { $v2.push(r); } else if $_self.$t3.$i3().any(|t| &t.name == r) { $v3.push(r); } else if $_self.groups.contains_key(r) { $gv.push(r); } } } } } } $v1.extend(&$tmp); }; } let mut tmp = vec![]; fill_vecs!(self { flags => c_flags => iter, opts => c_opt => iter, positionals => c_pos => values, grps, tmp }); tmp.clear(); fill_vecs!(self { opts => c_opt => iter, flags => c_flags => iter, positionals => c_pos => values, grps, tmp }); tmp.clear(); fill_vecs!(self { positionals => c_pos => values, opts => c_opt => iter, flags => c_flags => iter, grps, tmp }); let mut ret_val = VecDeque::new(); c_pos.dedup(); c_flags.dedup(); c_opt.dedup(); grps.dedup(); let args_in_groups = grps.iter() .flat_map(|g| self.arg_names_in_group(g)) .collect::>(); let pmap = c_pos.into_iter() .filter(|&p| matcher.is_none() || !matcher.as_ref().unwrap().contains(p)) .filter_map(|p| self.positionals.values().find(|x| x.name == p)) .filter(|p| !args_in_groups.contains(&p.name)) .map(|p| (p.index, p)) .collect::>();// sort by index debugln!("args_in_groups={:?}", args_in_groups); for &p in pmap.values() { let s = p.to_string(); if args_in_groups.is_empty() || !args_in_groups.contains(&&*s) { ret_val.push_back(s); } } macro_rules! write_arg { ($i:expr, $m:ident, $v:ident, $r:ident, $aig:ident) => { for f in $v { if $m.is_some() && $m.as_ref().unwrap().contains(f) || $aig.contains(&f) { continue; } $r.push_back($i.filter(|flg| &flg.name == &f).next().unwrap().to_string()); } } } write_arg!(self.flags.iter(), matcher, c_flags, ret_val, args_in_groups); write_arg!(self.opts.iter(), matcher, c_opt, ret_val, args_in_groups); let mut g_vec = vec![]; for g in grps { let g_string = self.args_in_group(g) .join("|"); g_vec.push(format!("<{}>", &g_string[..g_string.len()])); } g_vec.dedup(); for g in g_vec { ret_val.push_back(g); } ret_val } pub fn get_args_tag(&self) -> Option { let mut count = 0; 'outer: for p in self.positionals.values().filter(|p| !p.is_set(ArgSettings::Required)) { if let Some(g_vec) = self.groups_for_arg(p.name) { for grp_s in &g_vec { debugln!("iter;grp_s={};", grp_s); if let Some(grp) = self.groups.get(grp_s) { debug!("Is group required..."); if grp.required { sdebugln!("Yes (continuing)"); continue 'outer; } else { sdebugln!("No (breaking)"); } } } debugln!("Arg not required..."); count += 1; } else { debugln!("Arg not required..."); count += 1; } } if count > 1 || self.positionals.len() > 1 { return None; } else if count == 1 { let p = self.positionals.values().next().expect(INTERNAL_ERROR_MSG); return Some(format!(" [{}]{}", p.name_no_brackets(), p.multiple_str())); } Some("".into()) } pub fn needs_flags_tag(&self) -> bool { debugln!("fn=needs_flags_tag;"); 'outer: for f in &self.flags { debugln!("iter;f={};", f.name); if let Some(l) = f.long { if l == "help" || l == "version" { continue; } } if let Some(g_vec) = self.groups_for_arg(f.name) { for grp_s in &g_vec { debugln!("iter;grp_s={};", grp_s); if let Some(grp) = self.groups.get(grp_s) { debug!("Is group required..."); if grp.required { sdebugln!("Yes (continuing)"); continue 'outer; } else { sdebugln!("No (breaking)"); } } } debugln!("Flag not required...(returning true)"); return true; } else { debugln!("Flag not required...(returning true)"); return true; } } false } pub fn has_opts(&self) -> bool { !self.opts.is_empty() } pub fn has_flags(&self) -> bool { !self.flags.is_empty() } pub fn has_positionals(&self) -> bool { !self.positionals.is_empty() } pub fn has_subcommands(&self) -> bool { !self.subcommands.is_empty() } pub fn is_set(&self, s: AppSettings) -> bool { self.settings.is_set(s) } pub fn set(&mut self, s: AppSettings) { self.settings.set(s) } pub fn unset(&mut self, s: AppSettings) { self.settings.unset(s) } pub fn verify_positionals(&mut self) { // Because you must wait until all arguments have been supplied, this is the first chance // to make assertions on positional argument indexes // // Firt 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() { debug_assert!(!(idx != self.positionals.len()), format!("Found positional argument \"{}\" who's index is {} but there are \ only {} positional arguments defined", p.name, idx, self.positionals.len())); } // Next we verify that only the highest index has a .multiple(true) (if any) debug_assert!(!self.positionals .values() .any(|a| a.settings.is_set(ArgSettings::Multiple) && (a.index as usize != self.positionals.len()) ), "Only the positional argument with the highest index may accept multiple values"); // If it's required we also need to ensure all previous positionals are // required too let mut found = false; for p in self.positionals.values().rev() { if found { debug_assert!(p.settings.is_set(ArgSettings::Required), "Found positional argument which is not required with a lower index \ than a required positional argument: {:?} index {}", p.name, p.index); } else if p.settings.is_set(ArgSettings::Required) { found = true; continue; } } } pub fn propogate_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(a); } } sc.p.propogate_globals(); } } // Checks if the arg matches a subcommand name, or any of it's aliases (if defined) #[inline] fn possible_subcommand(&self, arg_os: &OsStr) -> bool { debugln!("fn=possible_subcommand"); self.subcommands .iter() .any(|s| { &s.p.meta.name[..] == &*arg_os || (s.p.meta.aliases.is_some() && s.p .meta .aliases .as_ref() .unwrap() .iter() .any(|&(a, _)| a == &*arg_os)) }) } #[inline] fn get_opt(&self, arg: &str) -> Option<&OptBuilder<'a, 'b>> { debugln!("fn=get_opt"); self.opts .iter() .find(|o| { &o.name == &arg || (o.aliases.is_some() && o.aliases .as_ref() .unwrap() .iter() .any(|&(a, _)| a == arg)) }) } fn parse_help_subcommand(&self, it: &mut I) -> ClapResult<()> where I: Iterator, T: Into { debugln!("fn=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.help = Some("The subcommand whose help message to display"); pb.set(ArgSettings::Multiple); sc.positionals.insert(1, pb); for s in self.g_settings.clone() { sc.set(s); } } 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)); } return sc._help(); } // The actual parsing function #[cfg_attr(feature = "lints", allow(while_let_on_iterator))] pub fn get_matches_with(&mut self, matcher: &mut ArgMatcher<'a>, it: &mut I) -> ClapResult<()> where I: Iterator, T: Into { debugln!("fn=get_matches_with;"); // First 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: Option<&str> = None; let mut pos_counter = 1; while let Some(arg) = it.next() { let arg_os = arg.into(); debugln!("Begin parsing '{:?}' ({:?})", arg_os, &*arg_os.as_bytes()); // Is this a new argument, or values from a previous option? debug!("Starts new arg..."); let starts_new_arg = if arg_os.starts_with(b"-") { sdebugln!("Maybe"); // a singe '-' by itself is a value and typically means "stdin" on unix systems !(arg_os.len_() == 1) } else { sdebugln!("No"); false }; // Has the user already passed '--'? Meaning only positional args follow if !self.trailing_vals { // Does the arg match a subcommand name, or any of it's aliases (if defined) let pos_sc = self.possible_subcommand(&arg_os); // If the arg doesn't start with a `-` (except numbers, or AllowLeadingHyphen) and // isn't a subcommand if (!starts_new_arg || (self.is_set(AppSettings::AllowLeadingHyphen) || self.is_set(AppSettings::AllowNegativeNumbers))) && !pos_sc { // Check to see if parsing a value from an option if let Some(arg) = needs_val_of { // get the OptBuilder so we can check the settings if let Some(ref opt) = self.get_opt(&arg) { needs_val_of = try!(self.add_val_to_arg(*opt, &arg_os, matcher)); // get the next value from the iterator continue; } } } if arg_os.starts_with(b"--") { if arg_os.len_() == 2 { // The user has passed '--' which means only positional args follow no // matter what they start with self.trailing_vals = true; continue; } needs_val_of = try!(self.parse_long_arg(matcher, &arg_os)); if !(needs_val_of.is_none() && self.is_set(AppSettings::AllowLeadingHyphen)) { 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 = try!(self.parse_short_arg(matcher, &arg_os)); // If it's None, we then check if one of those two AppSettings was set debugln!("AllowLeadingHyphen set...{:?}", self.is_set(AppSettings::AllowLeadingHyphen)); debugln!("AllowNegativeNumbers set...{:?}", self.is_set(AppSettings::AllowNegativeNumbers)); debugln!("Valid negative number...{:?}", (arg_os.to_string_lossy().parse::().is_ok() || arg_os.to_string_lossy().parse::().is_ok())); if needs_val_of.is_none() { if self.is_set(AppSettings::AllowNegativeNumbers) { 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(), "", &*self.create_current_usage(matcher), self.color())); } } else if !self.is_set(AppSettings::AllowLeadingHyphen) { continue; } } else { continue; } } if pos_sc { if &*arg_os == "help" && self.is_set(AppSettings::NeedsSubcommandHelp) { try!(self.parse_help_subcommand(it)); } subcmd_name = Some(arg_os.to_str().expect(INVALID_UTF8).to_owned()); break; } else if let Some(cdate) = suggestions::did_you_mean(&*arg_os.to_string_lossy(), self.subcommands .iter() .map(|s| &s.p.meta.name)) { return Err(Error::invalid_subcommand(arg_os.to_string_lossy().into_owned(), cdate, self.meta .bin_name .as_ref() .unwrap_or(&self.meta.name), &*self.create_current_usage(matcher), self.color())); } } if let Some(p) = self.positionals.get(pos_counter) { parse_positional!(self, p, arg_os, pos_counter, matcher); } else if self.settings.is_set(AppSettings::AllowExternalSubcommands) { // Get external subcommand name let sc_name = match arg_os.to_str() { Some(s) => s.to_string(), None => { if !self.settings.is_set(AppSettings::StrictUtf8) { return Err(Error::invalid_utf8(&*self.create_current_usage(matcher), 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 let None = a.to_str() { if !self.settings.is_set(AppSettings::StrictUtf8) { return Err(Error::invalid_utf8(&*self.create_current_usage(matcher), self.color())); } } sc_m.add_val_to("", &a); } matcher.subcommand(SubCommand { name: sc_name, matches: sc_m.into(), }); } else if !(self.is_set(AppSettings::AllowLeadingHyphen) || self.is_set(AppSettings::AllowNegativeNumbers)) { return Err(Error::unknown_argument(&*arg_os.to_string_lossy(), "", &*self.create_current_usage(matcher), self.color())); } } let mut reqs_validated = false; if let Some(a) = needs_val_of { debugln!("needs_val_of={}", a); debug!("Is this an opt..."); if let Some(o) = self.opts .iter() .find(|o| { &o.name == &a || (o.aliases.is_some() && o.aliases .as_ref() .unwrap() .iter() .any(|&(n, _)| n == &*a)) }) { sdebugln!("Yes"); try!(self.validate_required(matcher)); reqs_validated = true; let should_err = if let Some(v) = matcher.0.args.get(&*o.name) { v.vals.is_empty() && !(o.min_vals.is_some() && o.min_vals.unwrap() == 0) } else { true }; if should_err { return Err(Error::empty_value(o, &*self.create_current_usage(matcher), self.color())); } } else { sdebugln!("No"); debugln!("Returning Error::empty_value"); return Err(Error::empty_value(self.positionals .values() .find(|p| &p.name == &a) .expect(INTERNAL_ERROR_MSG), &*self.create_current_usage(matcher), self.color())); } } try!(self.add_defaults(matcher)); try!(self.validate_blacklist(matcher)); try!(self.validate_num_args(matcher)); matcher.usage(self.create_usage(&[])); if !(self.settings.is_set(AppSettings::SubcommandsNegateReqs) && subcmd_name.is_some()) && !reqs_validated { try!(self.validate_required(matcher)); } if let Some(pos_sc_name) = subcmd_name { // is this is a real subcommand, or an alias let sc_name = if self.subcommands.iter().any(|sc| sc.p.meta.name == pos_sc_name) { pos_sc_name } else { self.subcommands .iter() .filter(|sc| sc.p.meta.aliases.is_some()) .filter_map(|sc| if sc.p .meta .aliases .as_ref() .unwrap() .iter() .any(|&(a, _)| &a == &&*pos_sc_name) { Some(sc.p.meta.name.clone()) } else { None }) .next() .expect(INTERNAL_ERROR_MSG) }; try!(self.parse_subcommand(sc_name, matcher, it)); } else if self.is_set(AppSettings::SubcommandRequired) { let bn = self.meta.bin_name.as_ref().unwrap_or(&self.meta.name); return Err(Error::missing_subcommand(bn, &self.create_current_usage(matcher), self.color())); } else if self.is_set(AppSettings::SubcommandRequiredElseHelp) { let mut out = vec![]; try!(self.write_help_err(&mut out)); return Err(Error { message: String::from_utf8_lossy(&*out).into_owned(), kind: ErrorKind::MissingArgumentOrSubcommand, info: None, }); } if matcher.is_empty() && matcher.subcommand_name().is_none() && self.is_set(AppSettings::ArgRequiredElseHelp) { let mut out = vec![]; try!(self.write_help_err(&mut out)); return Err(Error { message: String::from_utf8_lossy(&*out).into_owned(), kind: ErrorKind::MissingArgumentOrSubcommand, info: None, }); } Ok(()) } fn propogate_help_version(&mut self) { debugln!("fn=propogate_help_version;"); self.create_help_and_version(); for sc in &mut self.subcommands { sc.p.propogate_help_version(); } } fn build_bin_names(&mut self) { debugln!("fn=build_bin_names;"); for sc in &mut self.subcommands { debug!("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!("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!("Calling build_bin_names from...{}", sc.p.meta.name); sc.p.build_bin_names(); } } fn parse_subcommand(&mut self, sc_name: String, matcher: &mut ArgMatcher<'a>, it: &mut I) -> ClapResult<()> where I: Iterator, T: Into { use std::fmt::Write; debugln!("fn=parse_subcommand;"); let mut mid_string = String::new(); if !self.settings.is_set(AppSettings::SubcommandsNegateReqs) { let mut hs: Vec<&str> = self.required.iter().map(|n| &**n).collect(); for k in matcher.arg_names() { hs.push(k); } let reqs = self.get_required_from(&hs, Some(matcher)); 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)); try!(sc.p.get_matches_with(&mut sc_matcher, it)); matcher.subcommand(SubCommand { name: sc.p.meta.name.clone(), matches: sc_matcher.into(), }); } Ok(()) } fn blacklisted_from(&self, name: &str, matcher: &ArgMatcher) -> Option { for k in matcher.arg_names() { if let Some(f) = self.flags.iter().find(|f| &f.name == &k) { if let Some(ref bl) = f.blacklist { if bl.contains(&name) { return Some(f.to_string()); } } } if let Some(o) = self.opts.iter().find(|o| &o.name == &k) { if let Some(ref bl) = o.blacklist { if bl.contains(&name) { return Some(o.to_string()); } } } if let Some(pos) = self.positionals.values().find(|p| &p.name == &k) { if let Some(ref bl) = pos.blacklist { if bl.contains(&name) { return Some(pos.name.to_owned()); } } } } None } fn overriden_from(&self, name: &str, matcher: &ArgMatcher) -> Option<&'a str> { for k in matcher.arg_names() { if let Some(f) = self.flags.iter().find(|f| &f.name == &k) { if let Some(ref bl) = f.overrides { if bl.contains(&name.into()) { return Some(f.name); } } } if let Some(o) = self.opts.iter().find(|o| &o.name == &k) { if let Some(ref bl) = o.overrides { if bl.contains(&name.into()) { return Some(o.name); } } } if let Some(pos) = self.positionals.values().find(|p| &p.name == &k) { if let Some(ref bl) = pos.overrides { if bl.contains(&name.into()) { return Some(pos.name); } } } } None } fn groups_for_arg(&self, name: &str) -> Option> { debugln!("fn=groups_for_arg; name={}", name); if self.groups.is_empty() { debugln!("No groups defined"); return None; } let mut res = vec![]; debugln!("Searching through groups..."); for (gn, grp) in &self.groups { for a in &grp.args { if a == &name { sdebugln!("\tFound '{}'", gn); res.push(*gn); } } } if res.is_empty() { return None; } Some(res) } fn args_in_group(&self, group: &str) -> Vec { let mut g_vec = vec![]; let mut args = vec![]; for n in &self.groups.get(group).unwrap().args { if let Some(f) = self.flags.iter().find(|f| &f.name == n) { args.push(f.to_string()); } else if let Some(f) = self.opts.iter().find(|o| &o.name == n) { args.push(f.to_string()); } else if self.groups.contains_key(&**n) { g_vec.push(*n); } else if let Some(p) = self.positionals .values() .find(|p| &p.name == n) { args.push(p.name.to_owned()); } } 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() } 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.get(group).unwrap().args { if self.groups.contains_key(&*n) { g_vec.push(*n); } else { args.push(*n); } } for av in g_vec.iter().map(|g| self.arg_names_in_group(g)) { args.extend(av); } args.dedup(); args.iter().map(|s| *s).collect() } pub fn create_help_and_version(&mut self) { debugln!("fn=create_help_and_version;"); // name is "hclap_help" because flags are sorted by name if self.is_set(AppSettings::NeedsLongHelp) { debugln!("Building --help"); if self.help_short.is_none() && !self.short_list.contains(&'h') { self.help_short = Some('h'); } let arg = FlagBuilder { name: "hclap_help", short: self.help_short, long: Some("help"), help: Some("Prints help information"), ..Default::default() }; if let Some(h) = self.help_short { self.short_list.push(h); } self.long_list.push("help"); self.flags.push(arg); } if !self.settings.is_set(AppSettings::DisableVersion) && self.is_set(AppSettings::NeedsLongVersion) { debugln!("Building --version"); if self.version_short.is_none() && !self.short_list.contains(&'V') { self.version_short = Some('V'); } // name is "vclap_version" because flags are sorted by name let arg = FlagBuilder { name: "vclap_version", short: self.version_short, long: Some("version"), help: Some("Prints version information"), ..Default::default() }; if let Some(v) = self.version_short { self.short_list.push(v); } self.long_list.push("version"); self.flags.push(arg); } if !self.subcommands.is_empty() && self.is_set(AppSettings::NeedsSubcommandHelp) { debugln!("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 pub fn create_current_usage(&self, matcher: &'b ArgMatcher<'a>) -> String { self.create_usage(&*matcher.arg_names() .iter() .filter(|n| { if let Some(o) = self.opts .iter() .find(|&o| &&o.name == n) { !o.settings.is_set(ArgSettings::Required) } else if let Some(p) = self.positionals .values() .find(|&p| &&p.name == n) { !p.settings.is_set(ArgSettings::Required) } else { true // flags can't be required, so they're always true } }) .map(|&n| n) .collect::>()) } fn check_for_help_and_version_str(&self, arg: &OsStr) -> ClapResult<()> { debug!("Checking if --{} is help or version...", arg.to_str().unwrap()); if arg == "help" && self.settings.is_set(AppSettings::NeedsLongHelp) { sdebugln!("Help"); try!(self._help()); } if arg == "version" && self.settings.is_set(AppSettings::NeedsLongVersion) { sdebugln!("Version"); try!(self._version()); } sdebugln!("Neither"); Ok(()) } fn check_for_help_and_version_char(&self, arg: char) -> ClapResult<()> { debug!("Checking if -{} is help or version...", arg); if let Some(h) = self.help_short { if arg == h && self.settings.is_set(AppSettings::NeedsLongHelp) { sdebugln!("Help"); try!(self._help()); } } if let Some(v) = self.version_short { if arg == v && self.settings.is_set(AppSettings::NeedsLongVersion) { sdebugln!("Version"); try!(self._version()); } } sdebugln!("Neither"); Ok(()) } fn _help(&self) -> ClapResult<()> { let mut buf = vec![]; try!(Help::write_parser_help(&mut buf, self)); // let out = io::stdout(); // let mut out_buf = BufWriter::new(out.lock()); // try!(out_buf.write(&*buf)); Err(Error { message: unsafe { String::from_utf8_unchecked(buf) }, kind: ErrorKind::HelpDisplayed, info: None, }) } fn _version(&self) -> ClapResult<()> { let out = io::stdout(); let mut buf_w = BufWriter::new(out.lock()); try!(self.print_version(&mut buf_w)); Err(Error { message: String::new(), kind: ErrorKind::VersionDisplayed, info: None, }) } fn parse_long_arg(&mut self, matcher: &mut ArgMatcher<'a>, full_arg: &OsStr) -> ClapResult> { // maybe here lifetime should be 'a debugln!("fn=parse_long_arg;"); let mut val = None; debug!("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) = self.opts .iter() .find(|v| { (v.long.is_some() && &*v.long.unwrap() == arg) || (v.aliases.is_some() && v.aliases .as_ref() .unwrap() .iter() .any(|&(n, _)| n == &*arg)) }) { debugln!("Found valid opt '{}'", opt.to_string()); let ret = try!(self.parse_opt(val, opt, matcher)); arg_post_processing!(self, opt, matcher); return Ok(ret); } else if let Some(flag) = self.flags .iter() .find(|v| { (v.long.is_some() && &*v.long.unwrap() == arg) || (v.aliases.is_some() && v.aliases .as_ref() .unwrap() .iter() .any(|&(n, _)| n == &*arg)) }) { debugln!("Found valid flag '{}'", flag.to_string()); // Only flags could be help or version, and we need to check the raw long // so this is the first point to check try!(self.check_for_help_and_version_str(&arg)); try!(self.parse_flag(flag, matcher)); // Handle conflicts, requirements, etc. arg_post_processing!(self, flag, matcher); return Ok(None); } else if self.is_set(AppSettings::AllowLeadingHyphen) { return Ok(None); } else if self.is_set(AppSettings::AllowNegativeNumbers) && (arg.to_string_lossy().parse::().is_ok() || arg.to_string_lossy().parse::().is_ok()) { return Ok(None); } debugln!("Didn't match anything"); self.did_you_mean_error(arg.to_str().expect(INVALID_UTF8), matcher).map(|_| None) } fn parse_short_arg(&mut self, matcher: &mut ArgMatcher<'a>, full_arg: &OsStr) -> ClapResult> { debugln!("fn=parse_short_arg;"); // let mut utf8 = true; let arg_os = full_arg.trim_left_matches(b'-'); let arg = arg_os.to_string_lossy(); for c in arg.chars() { // 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) = self.opts .iter() .find(|&v| v.short.is_some() && v.short.unwrap() == c) { debugln!("Found valid short opt -{} in '{}'", c, arg); // Check for trailing concatenated value let p: Vec<_> = arg.splitn(2, c).collect(); debugln!("arg: {:?}, arg_os: {:?}, full_arg: {:?}", arg, arg_os, full_arg); debugln!("p[0]: {:?}, p[1]: {:?}", 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!("setting val: {:?} (bytes), {:?} (ascii)", 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 = try!(self.parse_opt(val, opt, matcher)); arg_post_processing!(self, opt, matcher); return Ok(ret); } else if let Some(flag) = self.flags .iter() .find(|&v| v.short.is_some() && v.short.unwrap() == c) { debugln!("Found valid short flag -{}", c); // Only flags can be help or version try!(self.check_for_help_and_version_char(c)); try!(self.parse_flag(flag, matcher)); // Handle conflicts, requirements, overrides, etc. // Must be called here due to mutablilty arg_post_processing!(self, flag, matcher); } else if !(self.is_set(AppSettings::AllowLeadingHyphen) || self.is_set(AppSettings::AllowNegativeNumbers)) { let mut arg = String::new(); arg.push('-'); arg.push(c); return Err(Error::unknown_argument(&*arg, "", &*self.create_current_usage(matcher), self.color())); } } Ok(None) } fn parse_opt(&self, val: Option<&OsStr>, opt: &OptBuilder<'a, 'b>, matcher: &mut ArgMatcher<'a>) -> ClapResult> { debugln!("fn=parse_opt;"); validate_multiples!(self, opt, matcher); let mut has_eq = false; debug!("Checking for val..."); if let Some(fv) = val { has_eq = fv.starts_with(&[b'=']); let v = fv.trim_left_matches(b'='); if !opt.is_set(ArgSettings::EmptyValues) && v.len_() == 0 { sdebugln!("Found Empty - Error"); return Err(Error::empty_value(opt, &*self.create_current_usage(matcher), self.color())); } sdebugln!("Found - {:?}, len: {}", v, v.len_()); debugln!("{:?} contains '='...{:?}", fv, fv.starts_with(&[b'='])); try!(self.add_val_to_arg(opt, v, matcher)); } else { sdebugln!("None"); } matcher.inc_occurrence_of(opt.name); // Increment or create the group "args" self.groups_for_arg(opt.name).and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); if val.is_none() || !has_eq && (opt.is_set(ArgSettings::Multiple) && !opt.is_set(ArgSettings::RequireDelimiter) && matcher.needs_more_vals(opt)) { return Ok(Some(opt.name)); } Ok(None) } fn add_val_to_arg(&self, arg: &A, val: &OsStr, matcher: &mut ArgMatcher<'a>) -> ClapResult> where A: AnyArg<'a, 'b> + Display { debugln!("fn=add_val_to_arg;"); let mut ret = None; if !(self.trailing_vals && self.is_set(AppSettings::DontDelimitTrailingValues)) { if let Some(delim) = arg.val_delim() { if val.is_empty_() { ret = try!(self.add_single_val_to_arg(arg, val, matcher)); } else { for v in val.split(delim as u32 as u8) { ret = try!(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) { ret = None; } } } else { ret = try!(self.add_single_val_to_arg(arg, val, matcher)); } } else { ret = try!(self.add_single_val_to_arg(arg, val, matcher)); } Ok(ret) } fn add_single_val_to_arg(&self, arg: &A, v: &OsStr, matcher: &mut ArgMatcher<'a>) -> ClapResult> where A: AnyArg<'a, 'b> + Display { debugln!("adding val: {:?}", v); matcher.add_val_to(arg.name(), v); // 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); } } // The validation must come AFTER inserting into 'matcher' or the usage string // can't be built self.validate_value(arg, v, matcher) } fn validate_value(&self, arg: &A, val: &OsStr, matcher: &ArgMatcher<'a>) -> ClapResult> where A: AnyArg<'a, 'b> + Display { debugln!("fn=validate_value; val={:?}", val); if self.is_set(AppSettings::StrictUtf8) && val.to_str().is_none() { return Err(Error::invalid_utf8(&*self.create_current_usage(matcher), self.color())); } if let Some(p_vals) = arg.possible_vals() { let val_str = val.to_string_lossy(); if !p_vals.contains(&&*val_str) { return Err(Error::invalid_value(val_str, p_vals, arg, &*self.create_current_usage(matcher), self.color())); } } if !arg.is_set(ArgSettings::EmptyValues) && val.is_empty_() && matcher.contains(&*arg.name()) { return Err(Error::empty_value(arg, &*self.create_current_usage(matcher), self.color())); } if let Some(vtor) = arg.validator() { if let Err(e) = vtor(val.to_string_lossy().into_owned()) { return Err(Error::value_validation(e, self.color())); } } if matcher.needs_more_vals(arg) { return Ok(Some(arg.name())); } Ok(None) } fn parse_flag(&self, flag: &FlagBuilder<'a, 'b>, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> { debugln!("fn=parse_flag;"); validate_multiples!(self, flag, matcher); matcher.inc_occurrence_of(flag.name); // Increment or create the group "args" self.groups_for_arg(flag.name).and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); Ok(()) } fn validate_blacklist(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { debugln!("fn=validate_blacklist;"); macro_rules! build_err { ($me:ident, $name:expr, $matcher:ident) => ({ debugln!("macro=build_err;"); let c_with = $me.blacklisted_from($name, &$matcher); debugln!("'{:?}' conflicts with '{}'", c_with, $name); $matcher.remove($name); let usg = $me.create_current_usage($matcher); if let Some(f) = $me.flags.iter().filter(|f| f.name == $name).next() { debugln!("It was a flag..."); Error::argument_conflict(f, c_with, &*usg, self.color()) } else if let Some(o) = $me.opts.iter() .filter(|o| o.name == $name) .next() { debugln!("It was an option..."); Error::argument_conflict(o, c_with, &*usg, self.color()) } else { match $me.positionals.values() .filter(|p| p.name == $name) .next() { Some(p) => { debugln!("It was a positional..."); Error::argument_conflict(p, c_with, &*usg, self.color()) }, None => panic!(INTERNAL_ERROR_MSG) } } }); } for name in &self.blacklist { debugln!("Checking blacklisted name: {}", name); if self.groups.contains_key(name) { debugln!("groups contains it..."); for n in self.arg_names_in_group(name) { debugln!("Checking arg '{}' in group...", n); if matcher.contains(n) { debugln!("matcher contains it..."); return Err(build_err!(self, n, matcher)); } } } else if matcher.contains(name) { debugln!("matcher contains it..."); return Err(build_err!(self, *name, matcher)); } } Ok(()) } fn validate_num_args(&self, matcher: &mut ArgMatcher) -> ClapResult<()> { debugln!("fn=validate_num_args;"); for (name, ma) in matcher.iter() { if self.groups.contains_key(&**name) { continue; } else if let Some(opt) = self.opts .iter() .find(|o| &o.name == name) { try!(self._validate_num_vals(opt, ma, matcher)); } else if let Some(pos) = self.positionals .values() .find(|p| &p.name == name) { try!(self._validate_num_vals(pos, ma, matcher)); } } Ok(()) } fn _validate_num_vals(&self, a: &A, ma: &MatchedArg, matcher: &ArgMatcher) -> ClapResult<()> where A: AnyArg<'a, 'b> + Display { debugln!("fn=_validate_num_vals;"); if let Some(num) = a.num_vals() { debugln!("num_vals set: {}", num); let should_err = if a.is_set(ArgSettings::Multiple) { ((ma.vals.len() as u64) % num) != 0 } else { num != (ma.vals.len() as u64) }; if should_err { debugln!("Sending error WrongNumberOfValues"); return Err(Error::wrong_number_of_values(a, num, if a.is_set(ArgSettings::Multiple) { (ma.vals.len() % num as usize) } else { ma.vals.len() }, if ma.vals.len() == 1 || (a.is_set(ArgSettings::Multiple) && (ma.vals.len() % num as usize) == 1) { "as" } else { "ere" }, &*self.create_current_usage(matcher), self.color())); } } if let Some(num) = a.max_vals() { debugln!("max_vals set: {}", num); if (ma.vals.len() as u64) > num { debugln!("Sending error TooManyValues"); return Err(Error::too_many_values(ma.vals .get(ma.vals .keys() .last() .expect(INTERNAL_ERROR_MSG)) .expect(INTERNAL_ERROR_MSG) .to_str() .expect(INVALID_UTF8), a, &*self.create_current_usage(matcher), self.color())); } } if let Some(num) = a.min_vals() { debugln!("min_vals set: {}", num); if (ma.vals.len() as u64) < num { debugln!("Sending error TooFewValues"); return Err(Error::too_few_values(a, num, ma.vals.len(), &*self.create_current_usage(matcher), self.color())); } } Ok(()) } fn validate_required(&self, matcher: &ArgMatcher) -> ClapResult<()> { 'outer: for name in &self.required { if matcher.contains(name) { continue 'outer; } if let Some(grp) = self.groups.get(name) { for arg in &grp.args { if matcher.contains(arg) { continue 'outer; } } } if self.groups.values().any(|g| g.args.contains(name)) { continue 'outer; } if let Some(a) = self.flags.iter().find(|f| &f.name == name) { if self.is_missing_required_ok(a, matcher) { continue 'outer; } } else if let Some(a) = self.opts.iter().find(|o| &o.name == name) { if self.is_missing_required_ok(a, matcher) { continue 'outer; } } else if let Some(a) = self.positionals.values().find(|p| &p.name == name) { if self.is_missing_required_ok(a, matcher) { continue 'outer; } } let err = if self.settings.is_set(AppSettings::ArgRequiredElseHelp) && matcher.is_empty() { self._help().unwrap_err() } else { let mut reqs = self.required.iter().map(|&r| &*r).collect::>(); reqs.retain(|n| !matcher.contains(n)); reqs.dedup(); Error::missing_required_argument( &*self.get_required_from(&*reqs, Some(matcher)) .iter() .fold(String::new(), |acc, s| acc + &format!("\n {}", Format::Error(s))[..]), &*self.create_current_usage(matcher), self.color()) }; return Err(err); } Ok(()) } fn is_missing_required_ok(&self, a: &A, matcher: &ArgMatcher) -> bool where A: AnyArg<'a, 'b> { if let Some(bl) = a.blacklist() { for n in bl.iter() { if matcher.contains(n) || self.groups .get(n) .map_or(false, |g| g.args.iter().any(|an| matcher.contains(an))) { return true; } } } else if let Some(ru) = a.required_unless() { let mut found_any = false; for n in ru.iter() { if matcher.contains(n) || self.groups .get(n) .map_or(false, |g| g.args.iter().any(|an| matcher.contains(an))) { if !a.is_set(ArgSettings::RequiredUnlessAll) { return true; } found_any = true; } else if a.is_set(ArgSettings::RequiredUnlessAll) { return false; } } return found_any; } false } fn did_you_mean_error(&self, arg: &str, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> { // Didn't match a flag or option...maybe it was a typo and close to one let suffix = suggestions::did_you_mean_suffix(arg, self.long_list.iter(), suggestions::DidYouMeanMessageStyle::LongFlag); // Add the arg to the matches to build a proper usage string if let Some(name) = suffix.1 { if let Some(opt) = self.opts .iter() .find(|o| o.long.is_some() && o.long.unwrap() == name) { self.groups_for_arg(&*opt.name) .and_then(|grps| Some(matcher.inc_occurrences_of(&*grps))); matcher.insert(&*opt.name); } else if let Some(flg) = self.flags .iter() .find(|f| f.long.is_some() && f.long.unwrap() == name) { self.groups_for_arg(&*flg.name) .and_then(|grps| Some(matcher.inc_occurrences_of(&*grps))); matcher.insert(&*flg.name); } } let used_arg = format!("--{}", arg); Err(Error::unknown_argument(&*used_arg, &*suffix.0, &*self.create_current_usage(matcher), self.color())) } // Creates a usage string if one was not provided by the user manually. This happens just // after all arguments were parsed, but before any subcommands have been parsed // (so as to give subcommands their own usage recursively) pub fn create_usage(&self, used: &[&str]) -> String { debugln!("fn=create_usage;"); let mut usage = String::with_capacity(75); usage.push_str("USAGE:\n "); usage.push_str(&self.create_usage_no_title(used)); usage } // Creates a usage string (*without title*) if one was not provided by the user // manually. This happens just // after all arguments were parsed, but before any subcommands have been parsed // (so as to give subcommands their own usage recursively) pub fn create_usage_no_title(&self, used: &[&str]) -> String { debugln!("fn=create_usage_no_title;"); let mut usage = String::with_capacity(75); if let Some(u) = self.meta.usage_str { usage.push_str(&*u); } else if used.is_empty() { usage.push_str(&*self.meta .usage .as_ref() .unwrap_or(self.meta .bin_name .as_ref() .unwrap_or(&self.meta.name))); let mut reqs: Vec<&str> = self.required().map(|r| &**r).collect(); reqs.dedup(); let req_string = self.get_required_from(&reqs, None) .iter() .fold(String::new(), |a, s| a + &format!(" {}", s)[..]); let flags = self.needs_flags_tag(); if flags && !self.is_set(AppSettings::UnifiedHelpMessage) { usage.push_str(" [FLAGS]"); } else if flags { usage.push_str(" [OPTIONS]"); } if !self.is_set(AppSettings::UnifiedHelpMessage) && self.has_opts() && self.opts.iter().any(|a| !a.settings.is_set(ArgSettings::Required)) { usage.push_str(" [OPTIONS]"); } usage.push_str(&req_string[..]); // places a '--' in the usage string if there are args and options // supporting multiple values if self.has_positionals() && self.opts.iter().any(|a| a.settings.is_set(ArgSettings::Multiple)) && self.positionals.values().any(|a| !a.settings.is_set(ArgSettings::Required)) && !self.has_subcommands() { usage.push_str(" [--]") } if self.has_positionals() && self.positionals.values().any(|a| !a.settings.is_set(ArgSettings::Required)) { if let Some(args_tag) = self.get_args_tag() { usage.push_str(&*args_tag); } else { usage.push_str(" [ARGS]"); } } if self.has_subcommands() && !self.is_set(AppSettings::SubcommandRequired) { usage.push_str(" [SUBCOMMAND]"); } else if self.is_set(AppSettings::SubcommandRequired) && self.has_subcommands() { usage.push_str(" "); } } else { self.smart_usage(&mut usage, used); } usage.shrink_to_fit(); usage } // Creates a context aware usage string, or "smart usage" from currently used // args, and requirements fn smart_usage(&self, usage: &mut String, used: &[&str]) { debugln!("fn=smart_usage;"); let mut hs: Vec<&str> = self.required().map(|s| &**s).collect(); hs.extend_from_slice(used); let r_string = self.get_required_from(&hs, None) .iter() .fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]); usage.push_str(&self.meta .usage .as_ref() .unwrap_or(self.meta .bin_name .as_ref() .unwrap_or(&self.meta .name))[..]); usage.push_str(&*r_string); if self.is_set(AppSettings::SubcommandRequired) { usage.push_str(" "); } } // Prints the version to the user and exits if quit=true fn print_version(&self, w: &mut W) -> ClapResult<()> { try!(self.write_version(w)); w.flush().map_err(Error::from) } pub fn write_version(&self, w: &mut W) -> io::Result<()> { 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(" ", "-"), self.meta.version.unwrap_or("".into())) } else { write!(w, "{} {}", &self.meta.name[..], self.meta.version.unwrap_or("".into())) } } else { write!(w, "{} {}", &self.meta.name[..], self.meta.version.unwrap_or("".into())) } } 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) } pub fn write_help_err(&self, w: &mut W) -> ClapResult<()> { Help::write_parser_help_to_stderr(w, self) } fn add_defaults(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> { macro_rules! add_val { ($_self:ident, $a:ident, $m:ident) => { if $m.get($a.name).is_none() { try!($_self.add_val_to_arg($a, OsStr::new($a.default_val .as_ref() .unwrap()), $m)); arg_post_processing!($_self, $a, $m); } }; } for o in self.opts.iter().filter(|o| o.default_val.is_some()) { add_val!(self, o, matcher); } for p in self.positionals.values().filter(|p| p.default_val.is_some()) { add_val!(self, p, matcher); } Ok(()) } pub fn iter_flags(&self) -> Iter { self.flags.iter() } pub fn iter_opts(&self) -> Iter { self.opts.iter() } pub fn iter_positionals(&self) -> vec_map::Values { self.positionals.values() } // Should we color the output? None=determined by output location, true=yes, false=no #[doc(hidden)] pub fn color(&self) -> ColorWhen { debugln!("fn=color;"); debug!("Color setting..."); if self.is_set(AppSettings::ColorNever) { sdebugln!("Never"); ColorWhen::Never } else if self.is_set(AppSettings::ColorAlways) { sdebugln!("Always"); ColorWhen::Always } else { sdebugln!("Auto"); ColorWhen::Auto } } } impl<'a, 'b> Clone for Parser<'a, 'b> where 'a: 'b { fn clone(&self) -> Self { Parser { required: self.required.clone(), short_list: self.short_list.clone(), long_list: self.long_list.clone(), blacklist: self.blacklist.clone(), flags: self.flags.clone(), opts: self.opts.clone(), positionals: self.positionals.clone(), subcommands: self.subcommands.clone(), groups: self.groups.clone(), global_args: self.global_args.clone(), overrides: self.overrides.clone(), help_short: self.help_short, version_short: self.version_short, settings: self.settings.clone(), g_settings: self.g_settings.clone(), meta: self.meta.clone(), trailing_vals: self.trailing_vals, } } }