diff --git a/rustfmt.toml b/rustfmt.toml index 4905d27942b..0136d86e313 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -1,3 +1,4 @@ format_strings = false chain_overflow_last = false same_line_if_else = true +fn_single_line = true diff --git a/src/app/macros.rs b/src/app/macros.rs index 0f9f5ee54e0..d3c763192ec 100644 --- a/src/app/macros.rs +++ b/src/app/macros.rs @@ -1,6 +1,6 @@ macro_rules! remove_overriden { (@remove $_self:ident, $v:ident, $a:ident.$ov:ident) => { - if let Some(ref ora) = $a.$ov { + if let Some(ref ora) = $a.$ov() { vec_remove_all!($_self.$v, ora); } }; @@ -11,11 +11,11 @@ macro_rules! remove_overriden { }; ($_self:ident, $name:expr) => { debugln!("macro=remove_overriden!;"); - if let Some(ref o) = $_self.opts.iter().filter(|o| o.name == *$name).next() { + if let Some(ref o) = $_self.opts.iter().filter(|o| o.b.name == *$name).next() { remove_overriden!(@arg $_self, o); - } else if let Some(ref f) = $_self.flags.iter().filter(|f| f.name == *$name).next() { + } else if let Some(ref f) = $_self.flags.iter().filter(|f| f.b.name == *$name).next() { remove_overriden!(@arg $_self, f); - } else if let Some(p) = $_self.positionals.values().filter(|p| p.name == *$name).next() { + } else if let Some(p) = $_self.positionals.values().filter(|p| p.b.name == *$name).next() { remove_overriden!(@arg $_self, p); } }; @@ -55,20 +55,7 @@ macro_rules! arg_post_processing { if $matcher.contains(c) { sdebugln!("Yes"); // find who blacklisted us... - $me.blacklist.push(&$arg.name); - // if let Some(f) = $me.find_flag_mut(c) { - // if let Some(ref mut bl) = f.blacklist { - // bl.push(&$arg.name); - // } - // } else if let Some(o) = $me.find_option_mut(c) { - // if let Some(ref mut bl) = o.blacklist { - // bl.push(&$arg.name); - // } - // } else if let Some(p) = $me.find_positional_mut(c) { - // if let Some(ref mut bl) = p.blacklist { - // bl.push(&$arg.name); - // } - // } + $me.blacklist.push(&$arg.b.name); } else { sdebugln!("No"); } @@ -130,7 +117,7 @@ macro_rules! _handle_group_reqs{ macro_rules! validate_multiples { ($_self:ident, $a:ident, $m:ident) => { debugln!("macro=validate_multiples!;"); - if $m.contains(&$a.name) && !$a.settings.is_set(ArgSettings::Multiple) { + if $m.contains(&$a.b.name) && !$a.b.settings.is_set(ArgSettings::Multiple) { // Not the first time, and we don't allow multiples return Err(Error::unexpected_multiple_usage($a, &*$_self.create_current_usage($m), @@ -159,12 +146,12 @@ macro_rules! parse_positional { return Err(e); } - $matcher.inc_occurrence_of($p.name); - let _ = $_self.groups_for_arg($p.name) + $matcher.inc_occurrence_of($p.b.name); + let _ = $_self.groups_for_arg($p.b.name) .and_then(|vec| Some($matcher.inc_occurrences_of(&*vec))); arg_post_processing!($_self, $p, $matcher); // Only increment the positional counter if it doesn't allow multiples - if !$p.settings.is_set(ArgSettings::Multiple) { + if !$p.b.settings.is_set(ArgSettings::Multiple) { $pos_counter += 1; } }; @@ -174,24 +161,24 @@ macro_rules! find_from { ($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{ let mut ret = None; for k in $matcher.arg_names() { - if let Some(f) = $_self.find_flag(k) { - if let Some(ref v) = f.$from { + if let Some(f) = find_by_name!($_self, &k, flags, iter) { + if let Some(ref v) = f.$from() { if v.contains($arg_name) { ret = Some(f.to_string()); } } } - if let Some(o) = $_self.find_option(k) { - if let Some(ref v) = o.$from { + if let Some(o) = find_by_name!($_self, &k, opts, iter) { + if let Some(ref v) = o.$from() { if v.contains(&$arg_name) { ret = Some(o.to_string()); } } } - if let Some(pos) = $_self.find_positional(k) { - if let Some(ref v) = pos.$from { + if let Some(pos) = find_by_name!($_self, &k, positionals, values) { + if let Some(ref v) = pos.$from() { if v.contains($arg_name) { - ret = Some(pos.name.to_owned()); + ret = Some(pos.b.name.to_owned()); } } } @@ -204,28 +191,54 @@ macro_rules! find_name_from { ($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{ let mut ret = None; for k in $matcher.arg_names() { - if let Some(f) = $_self.find_flag(k) { - if let Some(ref v) = f.$from { + if let Some(f) = find_by_name!($_self, &k, flags, iter) { + if let Some(ref v) = f.$from() { if v.contains($arg_name) { - ret = Some(f.name); + ret = Some(f.b.name); } } } - if let Some(o) = $_self.find_option(k) { - if let Some(ref v) = o.$from { + if let Some(o) = find_by_name!($_self, &k, opts, iter) { + if let Some(ref v) = o.$from() { if v.contains(&$arg_name) { - ret = Some(o.name); + ret = Some(o.b.name); } } } - if let Some(pos) = $_self.find_positional(k) { - if let Some(ref v) = pos.$from { + if let Some(pos) = find_by_name!($_self, &k, positionals, values) { + if let Some(ref v) = pos.$from() { if v.contains($arg_name) { - ret = Some(pos.name); + ret = Some(pos.b.name); } } } } ret }}; -} \ No newline at end of file +} + +// Finds an option by name +macro_rules! find_by_name { + ($_self:ident, $name:expr, $what:ident, $how:ident) => { + $_self.$what.$how().find(|o| &o.b.name == $name) + } +} + +// Finds an option including if it's aliasesed +macro_rules! find_by_long { + ($_self:ident, $long:expr, $what:ident) => { + $_self.$what + .iter() + .filter(|o| o.s.long.is_some()) + .find(|o| { + &&o.s.long.unwrap() == &$long || + (o.s.aliases.is_some() && + o.s + .aliases + .as_ref() + .unwrap() + .iter() + .any(|&(alias, _)| &&alias == &$long)) + }) + } +} diff --git a/src/app/mod.rs b/src/app/mod.rs index 7dcb9312bd2..0a39cfcbf72 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -24,7 +24,7 @@ use yaml_rust::Yaml; // Internal use app::help::Help; use app::parser::Parser; -use args::{AnyArg, Arg, ArgGroup, ArgMatcher, ArgMatches, ArgSettings}; +use args::{ArgKind, AnyArg, Arg, ArgGroup, ArgMatcher, ArgMatches, ArgSettings}; use errors::Error; use errors::Result as ClapResult; pub use self::settings::AppSettings; @@ -1517,6 +1517,9 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> { fn name(&self) -> &'n str { unreachable!("App struct does not support AnyArg::name, this is a bug!") } + fn kind(&self) -> ArgKind { + ArgKind::Subcmd + } fn overrides(&self) -> Option<&[&'e str]> { None } diff --git a/src/app/parser.rs b/src/app/parser.rs index 60fa4b494cc..3939bf272d8 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -21,8 +21,8 @@ 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::{AnyArg, ArgMatcher, Base, Switched}; +use args::{Arg, ArgKind, ArgGroup, FlagBuilder, OptBuilder, PosBuilder}; use args::MatchedArg; use args::settings::ArgSettings; use completions::ComplGen; @@ -60,6 +60,8 @@ pub struct Parser<'a, 'b> pub g_settings: Vec, pub meta: AppMeta<'b>, trailing_vals: bool, + pub id: usize, + valid_neg_num: bool, } impl<'a, 'b> Default for Parser<'a, 'b> { @@ -82,6 +84,8 @@ impl<'a, 'b> Default for Parser<'a, 'b> { settings: AppFlags::new(), meta: AppMeta::new(), trailing_vals: false, + id: 0, + valid_neg_num: false, } } } @@ -134,11 +138,11 @@ impl<'a, 'b> Parser<'a, 'b> // 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)), + debug_assert!(!(self.flags.iter().any(|f| &f.b.name == &a.name) || + self.opts.iter().any(|o| o.b.name == a.name) || + self.positionals.values().any(|p| p.b.name == a.name)), format!("Non-unique argument name: {} is already in use", a.name)); - if let Some(ref grps) = a.group { + if let Some(ref grps) = a.groups { for g in grps { let ag = self.groups.entry(g).or_insert_with(|| ArgGroup::with_name(g)); ag.args.push(a.name); @@ -179,12 +183,16 @@ impl<'a, 'b> Parser<'a, 'b> self.positionals.insert(i, pb); } else if a.is_set(ArgSettings::TakesValue) { let mut ob = OptBuilder::from_arg(a, &mut self.required); - ob.unified_ord = self.flags.len() + self.opts.len(); - self.opts.push(ob); + let id = self.opts.len(); + ob.b.id = id; + ob.s.unified_ord = self.flags.len() + self.opts.len(); + self.opts.insert(id, ob); } else { let mut fb = FlagBuilder::from(a); - fb.unified_ord = self.flags.len() + self.opts.len(); - self.flags.push(fb); + let id = self.flags.len(); + fb.b.id = id; + fb.s.unified_ord = self.flags.len() + self.opts.len(); + self.flags.insert(id, fb); } if a.is_set(ArgSettings::Global) { debug_assert!(!a.is_set(ArgSettings::Required), @@ -264,14 +272,17 @@ impl<'a, 'b> Parser<'a, 'b> pub fn derive_display_order(&mut self) { if self.settings.is_set(AppSettings::DeriveDisplayOrder) { let unified = self.settings.is_set(AppSettings::UnifiedHelpMessage); - for (i, o) in self.opts.iter_mut().enumerate().filter(|&(_, ref o)| o.disp_ord == 999) { - o.disp_ord = if unified { o.unified_ord } else { i }; + 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.disp_ord == 999) { - f.disp_ord = if unified { f.unified_ord } else { i }; + .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() @@ -285,9 +296,7 @@ impl<'a, 'b> Parser<'a, 'b> } } - pub fn required(&self) -> Iter<&str> { - self.required.iter() - } + pub fn required(&self) -> Iter<&str> { self.required.iter() } pub fn get_required_from(&self, reqs: &[&'a str], @@ -298,9 +307,9 @@ impl<'a, 'b> Parser<'a, 'b> 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) { + if self.flags.iter().any(|f| &f.b.name == name) { c_flags.push(name); - } else if self.opts.iter().any(|o| &o.name == name) { + } else if self.opts.iter().any(|o| &o.b.name == name) { c_opt.push(name); } else if self.groups.contains_key(name) { grps.push(name); @@ -316,15 +325,15 @@ impl<'a, 'b> Parser<'a, 'b> $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 { + if let Some(a) = self.$t1.$i1().filter(|arg| &arg.b.name == a).next() { + if let Some(ref rl) = a.b.requires { for r in rl { if !reqs.contains(r) { - if $_self.$t1.$i1().any(|t| &t.name == r) { + if $_self.$t1.$i1().any(|t| &t.b.name == r) { $tmp.push(*r); - } else if $_self.$t2.$i2().any(|t| &t.name == r) { + } else if $_self.$t2.$i2().any(|t| &t.b.name == r) { $v2.push(r); - } else if $_self.$t3.$i3().any(|t| &t.name == r) { + } else if $_self.$t3.$i3().any(|t| &t.b.name == r) { $v3.push(r); } else if $_self.groups.contains_key(r) { $gv.push(r); @@ -370,8 +379,8 @@ impl<'a, 'b> Parser<'a, 'b> 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)) + .filter_map(|p| self.positionals.values().find(|x| x.b.name == p)) + .filter(|p| !args_in_groups.contains(&p.b.name)) .map(|p| (p.index, p)) .collect::>();// sort by index debugln!("args_in_groups={:?}", args_in_groups); @@ -387,7 +396,7 @@ impl<'a, 'b> Parser<'a, 'b> 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()); + $r.push_back($i.filter(|flg| &flg.b.name == &f).next().unwrap().to_string()); } } } @@ -410,7 +419,7 @@ impl<'a, 'b> Parser<'a, 'b> 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) { + if let Some(g_vec) = self.groups_for_arg(p.b.name) { for grp_s in &g_vec { debugln!("iter;grp_s={};", grp_s); if let Some(grp) = self.groups.get(grp_s) { @@ -441,14 +450,14 @@ impl<'a, 'b> Parser<'a, 'b> 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 { + 'outer: for f in self.flags.iter() { + debugln!("iter;f={};", f.b.name); + if let Some(l) = f.s.long { if l == "help" || l == "version" { continue; } } - if let Some(g_vec) = self.groups_for_arg(f.name) { + if let Some(g_vec) = self.groups_for_arg(f.b.name) { for grp_s in &g_vec { debugln!("iter;grp_s={};", grp_s); if let Some(grp) = self.groups.get(grp_s) { @@ -473,39 +482,25 @@ impl<'a, 'b> Parser<'a, 'b> } #[inline] - pub fn has_opts(&self) -> bool { - !self.opts.is_empty() - } + pub fn has_opts(&self) -> bool { !self.opts.is_empty() } #[inline] - pub fn has_flags(&self) -> bool { - !self.flags.is_empty() - } + pub fn has_flags(&self) -> bool { !self.flags.is_empty() } #[inline] - pub fn has_positionals(&self) -> bool { - !self.positionals.is_empty() - } + pub fn has_positionals(&self) -> bool { !self.positionals.is_empty() } #[inline] - pub fn has_subcommands(&self) -> bool { - !self.subcommands.is_empty() - } + pub fn has_subcommands(&self) -> bool { !self.subcommands.is_empty() } #[inline] - pub fn is_set(&self, s: AppSettings) -> bool { - self.settings.is_set(s) - } + pub fn is_set(&self, s: AppSettings) -> bool { self.settings.is_set(s) } #[inline] - pub fn set(&mut self, s: AppSettings) { - self.settings.set(s) - } + pub fn set(&mut self, s: AppSettings) { self.settings.set(s) } #[inline] - pub fn unset(&mut self, s: AppSettings) { - self.settings.unset(s) - } + pub fn unset(&mut self, s: AppSettings) { self.settings.unset(s) } #[cfg_attr(feature = "lints", allow(block_in_if_condition_stmt))] pub fn verify_positionals(&mut self) { @@ -519,23 +514,24 @@ impl<'a, 'b> Parser<'a, 'b> debug_assert!(!(idx != self.positionals.len()), format!("Found positional argument \"{}\" who's index is {} but there are \ only {} positional arguments defined", - p.name, + p.b.name, idx, self.positionals.len())); } // Next we verify that only the highest index has a .multiple(true) (if any) - if self.positionals() + if self.positionals + .values() .any(|a| { - a.settings.is_set(ArgSettings::Multiple) && + a.b.settings.is_set(ArgSettings::Multiple) && (a.index as usize != self.positionals.len()) }) { - debug_assert!(self.positionals() - .filter(|p| p.settings.is_set(ArgSettings::Multiple) - && p.num_vals.is_none()).map(|_| 1).sum::() <= 1, + debug_assert!(self.positionals.values() + .filter(|p| p.b.settings.is_set(ArgSettings::Multiple) + && p.v.num_vals.is_none()).map(|_| 1).sum::() <= 1, "Only one positional argument with .multiple(true) set is allowed per command"); - debug_assert!(self.positionals() + debug_assert!(self.positionals.values() .rev() .next() .unwrap() @@ -553,9 +549,9 @@ impl<'a, 'b> Parser<'a, 'b> self.set(AppSettings::LowIndexMultiplePositional); } - debug_assert!(self.positionals() - .filter(|p| p.settings.is_set(ArgSettings::Multiple) - && p.num_vals.is_none()) + debug_assert!(self.positionals.values() + .filter(|p| p.b.settings.is_set(ArgSettings::Multiple) + && p.v.num_vals.is_none()) .map(|_| 1) .sum::() <= 1, "Only one positional argument with .multiple(true) set is allowed per command"); @@ -565,12 +561,12 @@ impl<'a, 'b> Parser<'a, 'b> let mut found = false; for p in self.positionals.values().rev() { if found { - debug_assert!(p.settings.is_set(ArgSettings::Required), + debug_assert!(p.b.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.b.name, p.index); - } else if p.settings.is_set(ArgSettings::Required) { + } else if p.b.settings.is_set(ArgSettings::Required) { found = true; continue; } @@ -609,22 +605,6 @@ impl<'a, 'b> Parser<'a, 'b> }) } - #[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 @@ -683,7 +663,7 @@ impl<'a, 'b> Parser<'a, 'b> }; if help_help { let mut pb = PosBuilder::new("subcommand", 1); - pb.help = Some("The subcommand whose help message to display"); + pb.b.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() { @@ -698,6 +678,58 @@ impl<'a, 'b> Parser<'a, 'b> sc._help() } + #[inline] + fn check_is_new_arg(&mut self, arg_os: &OsStr, needs_val_of: Option<&'a str>) -> bool { + debugln!("fn=check_is_new_arg;nvo={:?}", needs_val_of); + let app_wide_settings = if self.is_set(AppSettings::AllowLeadingHyphen) { + true + } else if self.is_set(AppSettings::AllowNegativeNumbers) { + let a = arg_os.to_string_lossy(); + if a.parse::().is_ok() || a.parse::().is_ok() { + self.valid_neg_num = true; + true + } else { + false + } + } else { + false + }; + let arg_allows_tac = if let Some(name) = needs_val_of { + if let Some(o) = find_by_name!(self, &name, opts, iter) { + !(o.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings) + } else if let Some(p) = find_by_name!(self, &name, opts, iter) { + !(p.is_set(ArgSettings::AllowLeadingHyphen) || app_wide_settings) + } else { + true + } + } else { + true + }; + debugln!("Does arg allow leading hyphen...{:?}", !arg_allows_tac); + + // Is this a new argument, or values from a previous option? + debug!("Starts new arg..."); + let mut ret = if arg_os.starts_with(b"--") { + sdebugln!("--"); + if arg_os.len_() == 2 { + return true; // We have to return true so override everything else + } + true + } else if arg_os.starts_with(b"-") { + sdebugln!("-"); + // a singe '-' by itself is a value and typically means "stdin" on unix systems + !(arg_os.len_() == 1) + } else { + sdebugln!("Probable value"); + false + }; + + ret = ret && arg_allows_tac; + + debugln!("Starts new arg...{:?}", ret); + ret + } + // The actual parsing function #[cfg_attr(feature = "lints", allow(while_let_on_iterator))] pub fn get_matches_with(&mut self, @@ -716,14 +748,15 @@ impl<'a, 'b> Parser<'a, 'b> self.create_help_and_version(); let mut subcmd_name: Option = None; - let mut needs_val_of: Option<&str> = None; + let mut needs_val_of: Option<&'a 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()); + self.valid_neg_num = false; // Is this a new argument, or values from a previous option? - let starts_new_arg = is_new_arg(&arg_os); + let starts_new_arg = self.check_is_new_arg(&arg_os, needs_val_of); // Has the user already passed '--'? Meaning only positional args follow if !self.trailing_vals { @@ -732,15 +765,12 @@ impl<'a, 'b> Parser<'a, 'b> // 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 { + if !starts_new_arg && !pos_sc { // Check to see if parsing a value from an option - if let Some(arg) = needs_val_of { + if let Some(name) = needs_val_of { // get the OptBuilder so we can check the settings - if let Some(opt) = self.get_opt(arg) { - needs_val_of = try!(self.add_val_to_arg(&*opt, &arg_os, matcher)); + if let Some(arg) = find_by_name!(self, &name, opts, iter) { + needs_val_of = try!(self.add_val_to_arg(&*arg, &arg_os, matcher)); // get the next value from the iterator continue; } @@ -764,10 +794,6 @@ impl<'a, 'b> Parser<'a, 'b> // 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() || @@ -814,7 +840,7 @@ impl<'a, 'b> Parser<'a, 'b> sdebugln!("Found"); if let Some(na) = it.peek() { let n = (*na).clone().into(); - if is_new_arg(&n) || self.possible_subcommand(&n) || + if self.check_is_new_arg(&n, needs_val_of) || self.possible_subcommand(&n) || suggestions::did_you_mean(&n.to_string_lossy(), self.subcommands .iter() @@ -850,8 +876,8 @@ impl<'a, 'b> Parser<'a, 'b> while let Some(v) = it.next() { let a = v.into(); if a.to_str().is_none() && !self.settings.is_set(AppSettings::StrictUtf8) { - return Err(Error::invalid_utf8(&*self.create_current_usage(matcher), - self.color())); + return Err(Error::invalid_utf8(&*self.create_current_usage(matcher), + self.color())); } sc_m.add_val_to("", &a); } @@ -869,41 +895,31 @@ impl<'a, 'b> Parser<'a, 'b> } } + self.validate(needs_val_of, subcmd_name, matcher, it) + } + + fn validate(&mut self, + needs_val_of: Option<&'a str>, + subcmd_name: Option, + matcher: &mut ArgMatcher<'a>, + it: &mut Peekable) + -> ClapResult<()> + where I: Iterator, + T: Into + Clone + { 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())); - } + debugln!("needs_val_of={:?}", a); + let o = find_by_name!(self, &a, opts, iter).expect(INTERNAL_ERROR_MSG); + try!(self.validate_required(matcher)); + reqs_validated = true; + let should_err = if let Some(v) = matcher.0.args.get(&*o.b.name) { + v.vals.is_empty() && !(o.v.min_vals.is_some() && o.v.min_vals.unwrap() == 0) } 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), + true + }; + if should_err { + return Err(Error::empty_value(o, &*self.create_current_usage(matcher), self.color())); } @@ -1089,17 +1105,17 @@ impl<'a, 'b> Parser<'a, 'b> 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) { + for n in &self.groups[group].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.name == n) { + } else if let Some(f) = self.opts.iter().find(|o| &o.b.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()); + .find(|p| &p.b.name == n) { + args.push(p.b.name.to_owned()); } } @@ -1114,7 +1130,7 @@ impl<'a, 'b> Parser<'a, 'b> let mut g_vec = vec![]; let mut args = vec![]; - for n in &self.groups.get(group).unwrap().args { + for n in &self.groups[group].args { if self.groups.contains_key(&*n) { g_vec.push(*n); } else { @@ -1137,18 +1153,25 @@ impl<'a, 'b> Parser<'a, 'b> if self.help_short.is_none() && !self.short_list.contains(&'h') { self.help_short = Some('h'); } + let id = self.flags.len(); let arg = FlagBuilder { - name: "hclap_help", - short: self.help_short, - long: Some("help"), - help: Some("Prints help information"), - ..Default::default() + b: Base { + name: "hclap_help", + help: Some("Prints help information"), + id: id, + ..Default::default() + }, + s: Switched { + short: self.help_short, + long: Some("help"), + ..Default::default() + }, }; if let Some(h) = self.help_short { self.short_list.push(h); } self.long_list.push("help"); - self.flags.push(arg); + self.flags.insert(id, arg); } if !self.settings.is_set(AppSettings::DisableVersion) && self.is_set(AppSettings::NeedsLongVersion) { @@ -1156,19 +1179,26 @@ impl<'a, 'b> Parser<'a, 'b> if self.version_short.is_none() && !self.short_list.contains(&'V') { self.version_short = Some('V'); } + let id = self.flags.len(); // 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() + b: Base { + name: "vclap_version", + help: Some("Prints version information"), + id: id, + ..Default::default() + }, + s: Switched { + short: self.version_short, + long: Some("version"), + ..Default::default() + }, }; if let Some(v) = self.version_short { self.short_list.push(v); } self.long_list.push("version"); - self.flags.push(arg); + self.flags.insert(id, arg); } if !self.subcommands.is_empty() && self.is_set(AppSettings::NeedsSubcommandHelp) { debugln!("Building help"); @@ -1184,14 +1214,10 @@ impl<'a, 'b> Parser<'a, 'b> 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) + if let Some(o) = find_by_name!(self, *n, opts, iter) { + !o.b.settings.is_set(ArgSettings::Required) + } else if let Some(p) = find_by_name!(self, *n, positionals, values) { + !p.b.settings.is_set(ArgSettings::Required) } else { true // flags can't be required, so they're always true } @@ -1258,7 +1284,7 @@ impl<'a, 'b> Parser<'a, 'b> fn parse_long_arg(&mut self, matcher: &mut ArgMatcher<'a>, full_arg: &OsStr) - -> ClapResult> { + -> ClapResult> { // maybe here lifetime should be 'a debugln!("fn=parse_long_arg;"); let mut val = None; @@ -1273,33 +1299,13 @@ impl<'a, 'b> Parser<'a, 'b> 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)) - }) { + if let Some(opt) = find_by_long!(self, &arg, opts) { 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)) - }) { + } else if let Some(flag) = find_by_long!(self, &arg, flags) { 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 @@ -1313,9 +1319,7 @@ impl<'a, 'b> Parser<'a, 'b> 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()) { + } else if self.valid_neg_num { return Ok(None); } @@ -1332,14 +1336,32 @@ impl<'a, 'b> Parser<'a, 'b> 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(AppSettings::AllowLeadingHyphen) { + let mut good = true; + for c in arg.chars() { + good = self.short_list.contains(&c); + } + if !good { + return Ok(None); + } + } else if self.valid_neg_num { + // 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!("Valid negative num..."); + return Ok(None); + } + for c in arg.chars() { + debugln!("iter;short={}", c); // 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) { + .find(|&o| o.s.short.is_some() && o.s.short.unwrap() == c) { debugln!("Found valid short opt -{} in '{}'", c, arg); // Check for trailing concatenated value let p: Vec<_> = arg.splitn(2, c).collect(); @@ -1366,7 +1388,7 @@ impl<'a, 'b> Parser<'a, 'b> return Ok(ret); } else if let Some(flag) = self.flags .iter() - .find(|&v| v.short.is_some() && v.short.unwrap() == c) { + .find(|&f| f.s.short.is_some() && f.s.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)); @@ -1374,8 +1396,7 @@ impl<'a, 'b> Parser<'a, 'b> // 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)) { + } else { let mut arg = String::new(); arg.push('-'); arg.push(c); @@ -1414,16 +1435,18 @@ impl<'a, 'b> Parser<'a, 'b> sdebugln!("None"); } - matcher.inc_occurrence_of(opt.name); + matcher.inc_occurrence_of(opt.b.name); // Increment or create the group "args" - self.groups_for_arg(opt.name).and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); + self.groups_for_arg(opt.b.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)); + debugln!("More arg vals required..."); + return Ok(Some(opt.b.name)); } + debugln!("More arg vals not required..."); Ok(None) } @@ -1524,9 +1547,9 @@ impl<'a, 'b> Parser<'a, 'b> debugln!("fn=parse_flag;"); validate_multiples!(self, flag, matcher); - matcher.inc_occurrence_of(flag.name); + matcher.inc_occurrence_of(flag.b.name); // Increment or create the group "args" - self.groups_for_arg(flag.name).and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); + self.groups_for_arg(flag.b.name).and_then(|vec| Some(matcher.inc_occurrences_of(&*vec))); Ok(()) } @@ -1537,38 +1560,23 @@ impl<'a, 'b> Parser<'a, 'b> ($me:ident, $name:expr, $matcher:ident) => ({ debugln!("macro=build_err;name={}", $name); let mut c_with = find_from!($me, $name, blacklist, &$matcher); - c_with = if c_with.is_none() { - if let Some(aa) = $me.find_any_arg($name) { - if let Some(bl) = aa.blacklist() { - if let Some(arg_name) = bl.iter().find(|arg| $matcher.contains(arg)) { - if let Some(aa) = $me.find_any_arg(arg_name) { - Some(aa.to_string()) - } else { - c_with - } - } else { - c_with - } - } else { - c_with - } - } else { - c_with - } - } else { - c_with - }; + c_with = c_with.or( + $me.find_any_arg($name).map_or(None, |aa| aa.blacklist()) + .map_or(None, |bl| bl.iter().find(|arg| $matcher.contains(arg))) + .map_or(None, |an| $me.find_any_arg(an)) + .map_or(None, |aa| Some(format!("{}", aa))) + ); debugln!("'{:?}' conflicts with '{}'", c_with, $name); $matcher.remove($name); let usg = $me.create_current_usage($matcher); - if let Some(f) = $me.find_flag($name) { + if let Some(f) = find_by_name!($me, $name, flags, iter) { debugln!("It was a flag..."); Error::argument_conflict(f, c_with, &*usg, self.color()) - } else if let Some(o) = $me.find_option($name) { + } else if let Some(o) = find_by_name!($me, $name, opts, iter) { debugln!("It was an option..."); Error::argument_conflict(o, c_with, &*usg, self.color()) } else { - match $me.find_positional($name) { + match find_by_name!($me, $name, positionals, values) { Some(p) => { debugln!("It was a positional..."); Error::argument_conflict(p, c_with, &*usg, self.color()) @@ -1578,6 +1586,7 @@ impl<'a, 'b> Parser<'a, 'b> } }); } + for name in &self.blacklist { debugln!("Checking blacklisted name: {}", name); if self.groups.contains_key(name) { @@ -1602,13 +1611,11 @@ impl<'a, 'b> Parser<'a, 'b> 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) { + } else if let Some(opt) = find_by_name!(self, name, opts, iter) { try!(self._validate_num_vals(opt, ma, matcher)); } else if let Some(pos) = self.positionals .values() - .find(|p| &p.name == name) { + .find(|p| &p.b.name == name) { try!(self._validate_num_vals(pos, ma, matcher)); } } @@ -1693,15 +1700,15 @@ impl<'a, 'b> Parser<'a, 'b> 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 let Some(a) = find_by_name!(self, name, flags, iter) { if self.is_missing_required_ok(a, matcher) { continue 'outer; } - } else if let Some(a) = self.opts.iter().find(|o| &o.name == name) { + } else if let Some(a) = find_by_name!(self, name, opts, iter) { if self.is_missing_required_ok(a, matcher) { continue 'outer; } - } else if let Some(a) = self.positionals.values().find(|p| &p.name == name) { + } else if let Some(a) = find_by_name!(self, name, positionals, values) { if self.is_missing_required_ok(a, matcher) { continue 'outer; } @@ -1767,18 +1774,14 @@ impl<'a, 'b> Parser<'a, 'b> // 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) + if let Some(opt) = find_by_long!(self, &name, opts) { + self.groups_for_arg(&*opt.b.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) + matcher.insert(&*opt.b.name); + } else if let Some(flg) = find_by_long!(self, &name, flags) { + self.groups_for_arg(&*flg.b.name) .and_then(|grps| Some(matcher.inc_occurrences_of(&*grps))); - matcher.insert(&*flg.name); + matcher.insert(&*flg.b.name); } } @@ -1830,7 +1833,7 @@ impl<'a, 'b> Parser<'a, 'b> usage.push_str(" [OPTIONS]"); } if !self.is_set(AppSettings::UnifiedHelpMessage) && self.has_opts() && - self.opts.iter().any(|a| !a.settings.is_set(ArgSettings::Required)) { + self.opts.iter().any(|o| !o.b.settings.is_set(ArgSettings::Required)) { usage.push_str(" [OPTIONS]"); } @@ -1839,13 +1842,13 @@ impl<'a, 'b> Parser<'a, 'b> // 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.opts.iter().any(|o| o.b.settings.is_set(ArgSettings::Multiple)) && + self.positionals.values().any(|p| !p.b.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)) { + self.positionals.values().any(|p| !p.b.settings.is_set(ArgSettings::Required)) { if let Some(args_tag) = self.get_args_tag() { usage.push_str(&*args_tag); } else { @@ -1937,38 +1940,31 @@ impl<'a, 'b> Parser<'a, 'b> 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 + if $m.get($a.b.name).is_none() { + try!($_self.add_val_to_arg($a, OsStr::new($a.v.default_val .as_ref() - .unwrap()), $m)); + .unwrap()), + $m)); arg_post_processing!($_self, $a, $m); } }; } - for o in self.opts.iter().filter(|o| o.default_val.is_some()) { + for o in self.opts.iter().filter(|o| o.v.default_val.is_some()) { add_val!(self, o, matcher); } - for p in self.positionals.values().filter(|p| p.default_val.is_some()) { + for p in self.positionals.values().filter(|p| p.v.default_val.is_some()) { add_val!(self, p, matcher); } Ok(()) } - pub fn flags(&self) -> Iter> { - self.flags.iter() - } + pub fn flags(&self) -> Iter> { self.flags.iter() } - pub fn opts(&self) -> Iter> { - self.opts.iter() - } + pub fn opts(&self) -> Iter> { self.opts.iter() } - pub fn positionals(&self) -> vec_map::Values> { - self.positionals.values() - } + pub fn positionals(&self) -> vec_map::Values> { self.positionals.values() } - pub fn subcommands(&self) -> Iter { - self.subcommands.iter() - } + pub fn subcommands(&self) -> Iter { self.subcommands.iter() } // Should we color the output? None=determined by output location, true=yes, false=no #[doc(hidden)] @@ -1987,48 +1983,19 @@ impl<'a, 'b> Parser<'a, 'b> } } - pub fn find_any_arg(&self, arg: &str) -> Option<&AnyArg> { - if let Some(f) = self.find_flag(arg) { + pub fn find_any_arg(&self, name: &str) -> Option<&AnyArg> { + if let Some(f) = find_by_name!(self, &name, flags, iter) { return Some(f); } - if let Some(o) = self.find_option(arg) { + if let Some(o) = find_by_name!(self, &name, opts, iter) { return Some(o); } - if let Some(p) = self.find_positional(arg) { + if let Some(p) = find_by_name!(self, &name, positionals, values) { return Some(p); } None } - fn find_flag(&self, name: &str) -> Option<&FlagBuilder<'a, 'b>> { - for f in self.flags() { - if f.name == name || - f.aliases.as_ref().unwrap_or(&vec![("",false)]).iter().any(|&(n, _)| n == name) { - return Some(f); - } - } - None - } - - fn find_option(&self, name: &str) -> Option<&OptBuilder<'a, 'b>> { - for o in self.opts() { - if o.name == name || - o.aliases.as_ref().unwrap_or(&vec![("",false)]).iter().any(|&(n, _)| n == name) { - return Some(o); - } - } - None - } - - fn find_positional(&self, name: &str) -> Option<&PosBuilder<'a, 'b>> { - for p in self.positionals() { - if p.name == name { - return Some(p); - } - } - None - } - #[cfg_attr(feature = "lints", allow(explicit_iter_loop))] pub fn find_subcommand(&'b self, sc: &str) -> Option<&'b App<'a, 'b>> { debugln!("fn=find_subcommand;"); @@ -2076,20 +2043,8 @@ impl<'a, 'b> Clone for Parser<'a, 'b> g_settings: self.g_settings.clone(), meta: self.meta.clone(), trailing_vals: self.trailing_vals, + id: self.id, + valid_neg_num: self.valid_neg_num, } } } - -#[inline] -fn is_new_arg(arg_os: &OsStr) -> bool { - // Is this a new argument, or values from a previous option? - debug!("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 - } -} diff --git a/src/args/any_arg.rs b/src/args/any_arg.rs index 094082306fe..64dc578e4ec 100644 --- a/src/args/any_arg.rs +++ b/src/args/any_arg.rs @@ -7,6 +7,7 @@ use vec_map::VecMap; // Internal use args::settings::ArgSettings; +use args::ArgKind; #[doc(hidden)] pub trait AnyArg<'n, 'e>: std_fmt::Display { @@ -32,6 +33,7 @@ pub trait AnyArg<'n, 'e>: std_fmt::Display { fn help(&self) -> Option<&'e str>; fn default_val(&self) -> Option<&'n str>; fn longest_filter(&self) -> bool; + fn kind(&self) -> ArgKind; } pub trait DispOrder { diff --git a/src/args/arg.rs b/src/args/arg.rs index a598887626c..11fc06a3ba7 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -54,7 +54,7 @@ pub struct Arg<'a, 'b> #[doc(hidden)] pub requires: Option>, #[doc(hidden)] - pub group: Option>, + pub groups: Option>, #[doc(hidden)] pub val_names: Option>, #[doc(hidden)] @@ -91,7 +91,7 @@ impl<'a, 'b> Default for Arg<'a, 'b> { blacklist: None, possible_vals: None, requires: None, - group: None, + groups: None, val_names: None, num_vals: None, max_vals: None, @@ -172,6 +172,7 @@ impl<'a, 'b> Arg<'a, 'b> { "min_values" => yaml_to_u64!(a, v, min_values), "value_name" => yaml_to_str!(a, v, value_name), "use_delimiter" => yaml_to_bool!(a, v, use_delimiter), + "allow_hyphen_values" => yaml_to_bool!(a, v, allow_hyphen_values), "require_delimiter" => yaml_to_bool!(a, v, require_delimiter), "value_delimiter" => yaml_to_str!(a, v, value_delimiter), "required_unless" => yaml_to_str!(a, v, required_unless), @@ -636,6 +637,55 @@ impl<'a, 'b> Arg<'a, 'b> { } } + /// Allows values which start with a leading hyphen (`-`) + /// + /// # Examples + /// + /// ```rust + /// # use clap::Arg; + /// Arg::with_name("pattern") + /// .allow_hyphen_values(true) + /// # ; + /// ``` + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("pattest") + /// .arg(Arg::with_name("pat") + /// .allow_hyphen_values(true) + /// .takes_value(true) + /// .long("pattern")) + /// .get_matches_from(vec![ + /// "pattest", "--pattern", "-file" + /// ]); + /// + /// assert_eq!(m.value_of("pat"), Some("-file")); + /// ``` + /// + /// Not setting [`Arg::allow_hyphen_values(true)`] and supplying a value which starts with a + /// hyphen is an error. + /// + /// ```rust + /// # use clap::{App, Arg, ErrorKind}; + /// let res = App::new("pattest") + /// .arg(Arg::with_name("pat") + /// .takes_value(true) + /// .long("pattern")) + /// .get_matches_from_safe(vec![ + /// "pattest", "--pattern", "-file" + /// ]); + /// + /// assert!(res.is_err()); + /// assert_eq!(res.unwrap_err().kind, ErrorKind::UnknownArgument); + /// ``` + /// [`Arg::allow_hyphen_values(true)`]: ./struct.Arg.html#method.allow_hyphen_values + pub fn allow_hyphen_values(self, a: bool) -> Self { + if a { + self.set(ArgSettings::AllowLeadingHyphen) + } else { + self.unset(ArgSettings::AllowLeadingHyphen) + } + } /// Sets an arg that override this arg's required setting. (i.e. this arg will be required /// unless this other argument is present). /// @@ -1725,7 +1775,7 @@ impl<'a, 'b> Arg<'a, 'b> { if let Some(ref mut vec) = self.requires { vec.push(name); } else { - self.group = Some(vec![name]); + self.groups = Some(vec![name]); } self } @@ -1760,12 +1810,12 @@ impl<'a, 'b> Arg<'a, 'b> { /// ``` /// [`ArgGroup`]: ./struct.ArgGroup.html pub fn groups(mut self, names: &[&'a str]) -> Self { - if let Some(ref mut vec) = self.group { + if let Some(ref mut vec) = self.groups { for s in names { vec.push(s); } } else { - self.group = Some(names.into_iter().map(|s| *s).collect::>()); + self.groups = Some(names.into_iter().map(|s| *s).collect::>()); } self } @@ -2510,7 +2560,7 @@ impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for Arg<'a, 'b> { min_vals: a.min_vals, max_vals: a.max_vals, val_names: a.val_names.clone(), - group: a.group.clone(), + groups: a.groups.clone(), validator: a.validator.clone(), overrides: a.overrides.clone(), settings: a.settings, @@ -2538,7 +2588,7 @@ impl<'a, 'b> Clone for Arg<'a, 'b> { min_vals: self.min_vals, max_vals: self.max_vals, val_names: self.val_names.clone(), - group: self.group.clone(), + groups: self.groups.clone(), validator: self.validator.clone(), overrides: self.overrides.clone(), settings: self.settings, diff --git a/src/args/arg_builder/base.rs b/src/args/arg_builder/base.rs new file mode 100644 index 00000000000..f45573048ce --- /dev/null +++ b/src/args/arg_builder/base.rs @@ -0,0 +1,59 @@ +use args::{ArgSettings, Arg, ArgFlags, ArgKind}; + +#[derive(Debug, Clone)] +pub struct Base<'a, 'b> where 'a: 'b { + pub name: &'a str, + pub id: usize, + pub kind: ArgKind, + pub help: Option<&'b str>, + pub blacklist: Option>, + pub settings: ArgFlags, + pub r_unless: Option>, + pub overrides: Option>, + pub groups: Option>, + pub requires: Option>, +} + +impl<'n, 'e> Default for Base<'n, 'e> { + fn default() -> Self { + Base { + name: "", + id: 0, + kind: ArgKind::Pos, + help: None, + blacklist: None, + settings: ArgFlags::new(), + r_unless: None, + overrides: None, + requires: None, + groups: None, + } + } +} + +impl<'n, 'e> Base<'n, 'e> { + pub fn new(name: &'n str) -> Self { + Base { name: name, ..Default::default() } + } + + pub fn set(&mut self, s: ArgSettings) { + self.settings.set(s); + } +} + +impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Base<'n, 'e> { + fn from(a: &'z Arg<'n, 'e>) -> Self { + Base { + name: a.name, + help: a.help, + id: 0, + kind: ArgKind::Pos, + blacklist: a.blacklist.clone(), + settings: a.settings.clone(), + r_unless: a.r_unless.clone(), + overrides: a.overrides.clone(), + requires: a.requires.clone(), + groups: a.groups.clone(), + } + } +} diff --git a/src/args/arg_builder/flag.rs b/src/args/arg_builder/flag.rs index 2f5c648bcde..c4dd96e8c39 100644 --- a/src/args/arg_builder/flag.rs +++ b/src/args/arg_builder/flag.rs @@ -9,181 +9,72 @@ use vec_map::VecMap; // Internal use Arg; -use args::{AnyArg, DispOrder}; -use args::settings::{ArgFlags, ArgSettings}; +use args::{ArgSettings, ArgKind, Base, Switched, AnyArg, DispOrder}; -#[derive(Debug)] +#[derive(Default, Clone, Debug)] #[doc(hidden)] -pub struct FlagBuilder<'n, 'e> { - pub name: &'n str, - pub long: Option<&'e str>, - pub aliases: Option>, - pub help: Option<&'e str>, - pub blacklist: Option>, - pub requires: Option>, - pub short: Option, - pub overrides: Option>, - pub settings: ArgFlags, - pub disp_ord: usize, - pub unified_ord: usize, -} - -impl<'n, 'e> Default for FlagBuilder<'n, 'e> { - fn default() -> Self { - FlagBuilder { - name: "", - long: None, - aliases: None, - help: None, - blacklist: None, - requires: None, - short: None, - overrides: None, - settings: ArgFlags::new(), - disp_ord: 999, - unified_ord: 999, - } - } +pub struct FlagBuilder<'n, 'e> + where 'n: 'e +{ + pub b: Base<'n, 'e>, + pub s: Switched<'e>, } impl<'n, 'e> FlagBuilder<'n, 'e> { - pub fn new(name: &'n str) -> Self { - FlagBuilder { name: name, ..Default::default() } - } + pub fn new(name: &'n str) -> Self { FlagBuilder { b: Base::new(name), ..Default::default() } } } impl<'a, 'b, 'z> From<&'z Arg<'a, 'b>> for FlagBuilder<'a, 'b> { fn from(a: &'z Arg<'a, 'b>) -> Self { - assert!(a.validator.is_none(), - format!("The argument '{}' has a validator set, yet was parsed as a flag. Ensure \ - .takes_value(true) or .index(u64) is set.", - a.name)); - assert!(a.possible_vals.is_none(), - format!("The argument '{}' cannot have a specific value set because it doesn't \ - have takes_value(true) set", - a.name)); - assert!(!a.is_set(ArgSettings::Required), - format!("The argument '{}' cannot be required because it's a flag, perhaps you \ - forgot takes_value(true)?", - a.name)); // No need to check for index() or takes_value() as that is handled above FlagBuilder { - name: a.name, - short: a.short, - long: a.long, - aliases: a.aliases.clone(), - help: a.help, - blacklist: a.blacklist.clone(), - overrides: a.overrides.clone(), - requires: a.requires.clone(), - settings: a.settings, - disp_ord: a.disp_ord, - ..Default::default() + b: Base::from(a), + s: Switched::from(a), } } } impl<'n, 'e> Display for FlagBuilder<'n, 'e> { fn fmt(&self, f: &mut Formatter) -> Result { - if let Some(l) = self.long { + if let Some(l) = self.s.long { try!(write!(f, "--{}", l)); } else { - try!(write!(f, "-{}", self.short.unwrap())); + try!(write!(f, "-{}", self.s.short.unwrap())); } Ok(()) } } -impl<'n, 'e> Clone for FlagBuilder<'n, 'e> { - fn clone(&self) -> Self { - FlagBuilder { - name: self.name, - short: self.short, - long: self.long, - aliases: self.aliases.clone(), - help: self.help, - blacklist: self.blacklist.clone(), - overrides: self.overrides.clone(), - requires: self.requires.clone(), - settings: self.settings, - disp_ord: self.disp_ord, - unified_ord: self.unified_ord, - } - } -} - impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> { - fn name(&self) -> &'n str { - self.name - } - fn overrides(&self) -> Option<&[&'e str]> { - self.overrides.as_ref().map(|o| &o[..]) - } - fn requires(&self) -> Option<&[&'e str]> { - self.requires.as_ref().map(|o| &o[..]) - } - fn blacklist(&self) -> Option<&[&'e str]> { - self.blacklist.as_ref().map(|o| &o[..]) - } - fn required_unless(&self) -> Option<&[&'e str]> { - None - } - fn is_set(&self, s: ArgSettings) -> bool { - self.settings.is_set(s) - } - fn has_switch(&self) -> bool { - true - } - fn takes_value(&self) -> bool { - false - } - fn set(&mut self, s: ArgSettings) { - self.settings.set(s) - } - fn max_vals(&self) -> Option { - None - } - fn val_names(&self) -> Option<&VecMap<&'e str>> { - None - } - fn num_vals(&self) -> Option { - None - } - fn possible_vals(&self) -> Option<&[&'e str]> { - None - } - fn validator(&self) -> Option<&Rc StdResult<(), String>>> { - None - } - fn min_vals(&self) -> Option { - None - } - fn short(&self) -> Option { - self.short - } - fn long(&self) -> Option<&'e str> { - self.long - } - fn val_delim(&self) -> Option { - None - } - fn help(&self) -> Option<&'e str> { - self.help - } - fn default_val(&self) -> Option<&'n str> { - None - } - fn longest_filter(&self) -> bool { - self.long.is_some() - } + fn name(&self) -> &'n str { self.b.name } + fn kind(&self) -> ArgKind { ArgKind::Flag } + fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) } + fn requires(&self) -> Option<&[&'e str]> { self.b.requires.as_ref().map(|o| &o[..]) } + fn blacklist(&self) -> Option<&[&'e str]> { self.b.blacklist.as_ref().map(|o| &o[..]) } + fn required_unless(&self) -> Option<&[&'e str]> { None } + fn is_set(&self, s: ArgSettings) -> bool { self.b.settings.is_set(s) } + fn has_switch(&self) -> bool { true } + fn takes_value(&self) -> bool { false } + fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) } + fn max_vals(&self) -> Option { None } + fn val_names(&self) -> Option<&VecMap<&'e str>> { None } + fn num_vals(&self) -> Option { None } + fn possible_vals(&self) -> Option<&[&'e str]> { None } + fn validator(&self) -> Option<&Rc StdResult<(), String>>> { None } + fn min_vals(&self) -> Option { None } + fn short(&self) -> Option { self.s.short } + fn long(&self) -> Option<&'e str> { self.s.long } + fn val_delim(&self) -> Option { None } + fn help(&self) -> Option<&'e str> { self.b.help } + fn default_val(&self) -> Option<&'n str> { None } + fn longest_filter(&self) -> bool { self.s.long.is_some() } fn aliases(&self) -> Option> { - if let Some(ref aliases) = self.aliases { - let vis_aliases: Vec<_> = - aliases.iter() - .filter_map(|&(n, v)| if v { Some(n) } else { None }) - .collect(); + if let Some(ref aliases) = self.s.aliases { + let vis_aliases: Vec<_> = aliases.iter() + .filter_map(|&(n, v)| if v { Some(n) } else { None }) + .collect(); if vis_aliases.is_empty() { None } else { @@ -196,9 +87,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> { } impl<'n, 'e> DispOrder for FlagBuilder<'n, 'e> { - fn disp_ord(&self) -> usize { - self.disp_ord - } + fn disp_ord(&self) -> usize { self.s.disp_ord } } #[cfg(test)] @@ -233,12 +122,8 @@ mod test { fn flagbuilder_display_multiple_aliases() { let mut f = FlagBuilder::new("flg"); f.short = Some('f'); - f.aliases = Some(vec![ - ("alias_not_visible", false), - ("f2", true), - ("f3", true), - ("f4", true) - ]); + f.aliases = + Some(vec![("alias_not_visible", false), ("f2", true), ("f3", true), ("f4", true)]); assert_eq!(&*format!("{}", f), "-f"); } } diff --git a/src/args/arg_builder/mod.rs b/src/args/arg_builder/mod.rs index 53ccc9e7aa3..549a43ee147 100644 --- a/src/args/arg_builder/mod.rs +++ b/src/args/arg_builder/mod.rs @@ -1,7 +1,13 @@ pub use self::flag::FlagBuilder; pub use self::option::OptBuilder; pub use self::positional::PosBuilder; +pub use self::base::Base; +pub use self::switched::Switched; +pub use self::valued::Valued; mod flag; mod positional; mod option; +mod base; +mod valued; +mod switched; \ No newline at end of file diff --git a/src/args/arg_builder/option.rs b/src/args/arg_builder/option.rs index 1452172fe43..238cb5f9bcf 100644 --- a/src/args/arg_builder/option.rs +++ b/src/args/arg_builder/option.rs @@ -7,113 +7,33 @@ use std::result::Result as StdResult; use vec_map::VecMap; // Internal -use args::{AnyArg, Arg, DispOrder}; -use args::settings::{ArgFlags, ArgSettings}; +use args::{ArgSettings, ArgKind, AnyArg, Base, Switched, Valued, Arg, DispOrder}; #[allow(missing_debug_implementations)] #[doc(hidden)] -pub struct OptBuilder<'n, 'e> { - pub name: &'n str, - pub short: Option, - pub long: Option<&'e str>, - pub aliases: Option>, - pub help: Option<&'e str>, - pub blacklist: Option>, - pub possible_vals: Option>, - pub requires: Option>, - pub num_vals: Option, - pub min_vals: Option, - pub max_vals: Option, - pub val_names: Option>, - pub validator: Option StdResult<(), String>>>, - pub overrides: Option>, - pub settings: ArgFlags, - pub val_delim: Option, - pub default_val: Option<&'n str>, - pub disp_ord: usize, - pub unified_ord: usize, - pub r_unless: Option>, -} - -impl<'n, 'e> Default for OptBuilder<'n, 'e> { - fn default() -> Self { - OptBuilder { - name: "", - short: None, - long: None, - aliases: None, - help: None, - blacklist: None, - possible_vals: None, - requires: None, - num_vals: None, - min_vals: None, - max_vals: None, - val_names: None, - validator: None, - overrides: None, - settings: ArgFlags::new(), - val_delim: Some(','), - default_val: None, - disp_ord: 999, - unified_ord: 999, - r_unless: None, - } - } +#[derive(Default, Clone)] +pub struct OptBuilder<'n, 'e> + where 'n: 'e +{ + pub b: Base<'n, 'e>, + pub s: Switched<'e>, + pub v: Valued<'n, 'e>, } impl<'n, 'e> OptBuilder<'n, 'e> { - pub fn new(name: &'n str) -> Self { - OptBuilder { name: name, ..Default::default() } - } + pub fn new(name: &'n str) -> Self { OptBuilder { b: Base::new(name), ..Default::default() } } pub fn from_arg(a: &Arg<'n, 'e>, reqs: &mut Vec<&'e str>) -> Self { - assert!(a.short.is_some() || a.long.is_some(), - format!("Argument \"{}\" has takes_value(true), yet neither a short() or long() \ - was supplied", - a.name)); - // No need to check for .index() as that is handled above - let mut ob = OptBuilder { - name: a.name, - short: a.short, - long: a.long, - aliases: a.aliases.clone(), - help: a.help, - num_vals: a.num_vals, - min_vals: a.min_vals, - max_vals: a.max_vals, - val_names: a.val_names.clone(), - val_delim: a.val_delim, - blacklist: a.blacklist.clone(), - overrides: a.overrides.clone(), - requires: a.requires.clone(), - possible_vals: a.possible_vals.clone(), - settings: a.settings, - default_val: a.default_val, - disp_ord: a.disp_ord, - r_unless: a.r_unless.clone(), - ..Default::default() + let ob = OptBuilder { + b: Base::from(a), + s: Switched::from(a), + v: Valued::from(a), }; - if let Some(ref vec) = ob.val_names { - if vec.len() > 1 { - ob.num_vals = Some(vec.len() as u64); - } - } - if let Some(ref vec) = ob.val_names { - if vec.len() > 1 { - ob.num_vals = Some(vec.len() as u64); - } - } - if let Some(ref p) = a.validator { - ob.validator = Some(p.clone()); - } // If the arg is required, add all it's requirements to master required list if a.is_set(ArgSettings::Required) { if let Some(ref areqs) = a.requires { - for r in areqs { - reqs.push(*r); - } + reqs.extend(areqs); } } ob @@ -124,14 +44,14 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> { fn fmt(&self, f: &mut Formatter) -> Result { debugln!("fn=fmt"); // Write the name such --long or -l - if let Some(l) = self.long { + if let Some(l) = self.s.long { try!(write!(f, "--{} ", l)); } else { - try!(write!(f, "-{} ", self.short.unwrap())); + try!(write!(f, "-{} ", self.s.short.unwrap())); } // Write the values such as - if let Some(ref vec) = self.val_names { + if let Some(ref vec) = self.v.val_names { let mut it = vec.iter().peekable(); while let Some((_, val)) = it.next() { try!(write!(f, "<{}>", val)); @@ -143,10 +63,10 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> { if self.is_set(ArgSettings::Multiple) && num == 1 { try!(write!(f, "...")); } - } else if let Some(num) = self.num_vals { + } else if let Some(num) = self.v.num_vals { let mut it = (0..num).peekable(); while let Some(_) = it.next() { - try!(write!(f, "<{}>", self.name)); + try!(write!(f, "<{}>", self.b.name)); if it.peek().is_some() { try!(write!(f, " ")); } @@ -154,7 +74,7 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> { } else { try!(write!(f, "<{}>{}", - self.name, + self.b.name, if self.is_set(ArgSettings::Multiple) { "..." } else { @@ -166,103 +86,36 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> { } } -impl<'n, 'e> Clone for OptBuilder<'n, 'e> { - fn clone(&self) -> Self { - OptBuilder { - name: self.name, - short: self.short, - long: self.long, - aliases: self.aliases.clone(), - help: self.help, - blacklist: self.blacklist.clone(), - overrides: self.overrides.clone(), - requires: self.requires.clone(), - settings: self.settings, - disp_ord: self.disp_ord, - unified_ord: self.unified_ord, - num_vals: self.num_vals, - min_vals: self.min_vals, - max_vals: self.max_vals, - val_names: self.val_names.clone(), - val_delim: self.val_delim, - possible_vals: self.possible_vals.clone(), - default_val: self.default_val, - validator: self.validator.clone(), - r_unless: self.r_unless.clone(), - } - } -} - impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> { - fn name(&self) -> &'n str { - self.name - } - fn overrides(&self) -> Option<&[&'e str]> { - self.overrides.as_ref().map(|o| &o[..]) - } - fn requires(&self) -> Option<&[&'e str]> { - self.requires.as_ref().map(|o| &o[..]) - } - fn blacklist(&self) -> Option<&[&'e str]> { - self.blacklist.as_ref().map(|o| &o[..]) - } - fn required_unless(&self) -> Option<&[&'e str]> { - self.r_unless.as_ref().map(|o| &o[..]) - } - fn val_names(&self) -> Option<&VecMap<&'e str>> { - self.val_names.as_ref() - } - fn is_set(&self, s: ArgSettings) -> bool { - self.settings.is_set(s) - } - fn has_switch(&self) -> bool { - true - } - fn set(&mut self, s: ArgSettings) { - self.settings.set(s) - } - fn max_vals(&self) -> Option { - self.max_vals - } - fn num_vals(&self) -> Option { - self.num_vals - } - fn possible_vals(&self) -> Option<&[&'e str]> { - self.possible_vals.as_ref().map(|o| &o[..]) - } + fn name(&self) -> &'n str { self.b.name } + fn kind(&self) -> ArgKind { ArgKind::Opt } + fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) } + fn requires(&self) -> Option<&[&'e str]> { self.b.requires.as_ref().map(|o| &o[..]) } + fn blacklist(&self) -> Option<&[&'e str]> { self.b.blacklist.as_ref().map(|o| &o[..]) } + fn required_unless(&self) -> Option<&[&'e str]> { self.b.r_unless.as_ref().map(|o| &o[..]) } + fn val_names(&self) -> Option<&VecMap<&'e str>> { self.v.val_names.as_ref() } + fn is_set(&self, s: ArgSettings) -> bool { self.b.settings.is_set(s) } + fn has_switch(&self) -> bool { true } + fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) } + fn max_vals(&self) -> Option { self.v.max_vals } + fn num_vals(&self) -> Option { self.v.num_vals } + fn possible_vals(&self) -> Option<&[&'e str]> { self.v.possible_vals.as_ref().map(|o| &o[..]) } fn validator(&self) -> Option<&Rc StdResult<(), String>>> { - self.validator.as_ref() - } - fn min_vals(&self) -> Option { - self.min_vals - } - fn short(&self) -> Option { - self.short - } - fn long(&self) -> Option<&'e str> { - self.long - } - fn val_delim(&self) -> Option { - self.val_delim - } - fn takes_value(&self) -> bool { - true - } - fn help(&self) -> Option<&'e str> { - self.help - } - fn default_val(&self) -> Option<&'n str> { - self.default_val - } - fn longest_filter(&self) -> bool { - true - } + self.v.validator.as_ref() + } + fn min_vals(&self) -> Option { self.v.min_vals } + fn short(&self) -> Option { self.s.short } + fn long(&self) -> Option<&'e str> { self.s.long } + fn val_delim(&self) -> Option { self.v.val_delim } + fn takes_value(&self) -> bool { true } + fn help(&self) -> Option<&'e str> { self.b.help } + fn default_val(&self) -> Option<&'n str> { self.v.default_val } + fn longest_filter(&self) -> bool { true } fn aliases(&self) -> Option> { - if let Some(ref aliases) = self.aliases { - let vis_aliases: Vec<_> = - aliases.iter() - .filter_map(|&(n, v)| if v { Some(n) } else { None }) - .collect(); + if let Some(ref aliases) = self.s.aliases { + let vis_aliases: Vec<_> = aliases.iter() + .filter_map(|&(n, v)| if v { Some(n) } else { None }) + .collect(); if vis_aliases.is_empty() { None } else { @@ -275,9 +128,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> { } impl<'n, 'e> DispOrder for OptBuilder<'n, 'e> { - fn disp_ord(&self) -> usize { - self.disp_ord - } + fn disp_ord(&self) -> usize { self.s.disp_ord } } #[cfg(test)] @@ -335,12 +186,8 @@ mod test { fn optbuilder_display_multiple_aliases() { let mut o = OptBuilder::new("opt"); o.long = Some("option"); - o.aliases = Some(vec![ - ("als_not_visible", false), - ("als2", true), - ("als3", true), - ("als4", true) - ]); + o.aliases = + Some(vec![("als_not_visible", false), ("als2", true), ("als3", true), ("als4", true)]); assert_eq!(&*format!("{}", o), "--option "); } } diff --git a/src/args/arg_builder/positional.rs b/src/args/arg_builder/positional.rs index ec8c66f069a..4b62938c6f3 100644 --- a/src/args/arg_builder/positional.rs +++ b/src/args/arg_builder/positional.rs @@ -9,112 +9,52 @@ use vec_map::VecMap; // Internal use Arg; -use args::{AnyArg, DispOrder}; -use args::settings::{ArgFlags, ArgSettings}; +use args::{ArgSettings, Base, Valued, ArgKind, AnyArg, DispOrder}; #[allow(missing_debug_implementations)] #[doc(hidden)] -pub struct PosBuilder<'n, 'e> { - pub name: &'n str, - pub help: Option<&'e str>, - pub requires: Option>, - pub blacklist: Option>, - pub possible_vals: Option>, +#[derive(Clone, Default)] +pub struct PosBuilder<'n, 'e> + where 'n: 'e +{ + pub b: Base<'n, 'e>, + pub v: Valued<'n, 'e>, pub index: u64, - pub num_vals: Option, - pub max_vals: Option, - pub min_vals: Option, - pub val_names: Option>, - pub validator: Option StdResult<(), String>>>, - pub overrides: Option>, - pub settings: ArgFlags, - pub val_delim: Option, - pub default_val: Option<&'n str>, - pub disp_ord: usize, - pub r_unless: Option>, -} - -impl<'n, 'e> Default for PosBuilder<'n, 'e> { - fn default() -> Self { - PosBuilder { - name: "", - help: None, - requires: None, - blacklist: None, - possible_vals: None, - index: 0, - num_vals: None, - min_vals: None, - max_vals: None, - val_names: None, - validator: None, - overrides: None, - settings: ArgFlags::new(), - val_delim: Some(','), - default_val: None, - disp_ord: 999, - r_unless: None, - } - } } impl<'n, 'e> PosBuilder<'n, 'e> { pub fn new(name: &'n str, idx: u64) -> Self { PosBuilder { - name: name, + b: Base::new(name), index: idx, ..Default::default() } } pub fn from_arg(a: &Arg<'n, 'e>, idx: u64, reqs: &mut Vec<&'e str>) -> Self { - debug_assert!(a.short.is_none() || a.long.is_none(), - format!("Argument \"{}\" has conflicting requirements, both index() and \ - short(), or long(), were supplied", - a.name)); - // Create the Positional Argument Builder with each HashSet = None to only // allocate // those that require it let mut pb = PosBuilder { - name: a.name, + b: Base::from(a), + v: Valued::from(a), index: idx, - num_vals: a.num_vals, - min_vals: a.min_vals, - max_vals: a.max_vals, - val_names: a.val_names.clone(), - blacklist: a.blacklist.clone(), - overrides: a.overrides.clone(), - requires: a.requires.clone(), - possible_vals: a.possible_vals.clone(), - help: a.help, - val_delim: a.val_delim, - settings: a.settings, - default_val: a.default_val, - disp_ord: a.disp_ord, - r_unless: a.r_unless.clone(), - ..Default::default() }; if a.max_vals.is_some() || a.min_vals.is_some() || (a.num_vals.is_some() && a.num_vals.unwrap() > 1) { - pb.settings.set(ArgSettings::Multiple); - } - if let Some(ref p) = a.validator { - pb.validator = Some(p.clone()); + pb.b.settings.set(ArgSettings::Multiple); } // If the arg is required, add all it's requirements to master required list if a.is_set(ArgSettings::Required) { if let Some(ref areqs) = a.requires { - for r in areqs { - reqs.push(*r); - } + reqs.extend(areqs); } } pb } pub fn multiple_str(&self) -> &str { - if self.settings.is_set(ArgSettings::Multiple) && self.val_names.is_none() { + if self.b.settings.is_set(ArgSettings::Multiple) && self.v.val_names.is_none() { "..." } else { "" @@ -122,20 +62,20 @@ impl<'n, 'e> PosBuilder<'n, 'e> { } pub fn name_no_brackets(&self) -> Cow { - if let Some(ref names) = self.val_names { + if let Some(ref names) = self.v.val_names { Cow::Owned(names.values() .map(|n| format!("<{}>", n)) .collect::>() .join(" ")) } else { - Cow::Borrowed(self.name) + Cow::Borrowed(self.b.name) } } } impl<'n, 'e> Display for PosBuilder<'n, 'e> { fn fmt(&self, f: &mut Formatter) -> Result { - if let Some(ref names) = self.val_names { + if let Some(ref names) = self.v.val_names { try!(write!(f, "{}", names.values() @@ -143,9 +83,9 @@ impl<'n, 'e> Display for PosBuilder<'n, 'e> { .collect::>() .join(" "))); } else { - try!(write!(f, "<{}>", self.name)); + try!(write!(f, "<{}>", self.b.name)); } - if self.settings.is_set(ArgSettings::Multiple) && self.val_names.is_none() { + if self.b.settings.is_set(ArgSettings::Multiple) && self.v.val_names.is_none() { try!(write!(f, "...")); } @@ -153,103 +93,36 @@ impl<'n, 'e> Display for PosBuilder<'n, 'e> { } } -impl<'n, 'e> Clone for PosBuilder<'n, 'e> { - fn clone(&self) -> Self { - PosBuilder { - name: self.name, - help: self.help, - blacklist: self.blacklist.clone(), - overrides: self.overrides.clone(), - requires: self.requires.clone(), - settings: self.settings, - disp_ord: self.disp_ord, - num_vals: self.num_vals, - min_vals: self.min_vals, - max_vals: self.max_vals, - val_names: self.val_names.clone(), - val_delim: self.val_delim, - possible_vals: self.possible_vals.clone(), - default_val: self.default_val, - validator: self.validator.clone(), - r_unless: self.r_unless.clone(), - index: self.index, - } - } -} - impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> { - fn name(&self) -> &'n str { - self.name - } - fn overrides(&self) -> Option<&[&'e str]> { - self.overrides.as_ref().map(|o| &o[..]) - } - fn requires(&self) -> Option<&[&'e str]> { - self.requires.as_ref().map(|o| &o[..]) - } - fn blacklist(&self) -> Option<&[&'e str]> { - self.blacklist.as_ref().map(|o| &o[..]) - } - fn required_unless(&self) -> Option<&[&'e str]> { - self.r_unless.as_ref().map(|o| &o[..]) - } - fn val_names(&self) -> Option<&VecMap<&'e str>> { - self.val_names.as_ref() - } - fn is_set(&self, s: ArgSettings) -> bool { - self.settings.is_set(s) - } - fn set(&mut self, s: ArgSettings) { - self.settings.set(s) - } - fn has_switch(&self) -> bool { - false - } - fn max_vals(&self) -> Option { - self.max_vals - } - fn num_vals(&self) -> Option { - self.num_vals - } - fn possible_vals(&self) -> Option<&[&'e str]> { - self.possible_vals.as_ref().map(|o| &o[..]) - } + fn name(&self) -> &'n str { self.b.name } + fn kind(&self) -> ArgKind { ArgKind::Pos } + fn overrides(&self) -> Option<&[&'e str]> { self.b.overrides.as_ref().map(|o| &o[..]) } + fn requires(&self) -> Option<&[&'e str]> { self.b.requires.as_ref().map(|o| &o[..]) } + fn blacklist(&self) -> Option<&[&'e str]> { self.b.blacklist.as_ref().map(|o| &o[..]) } + fn required_unless(&self) -> Option<&[&'e str]> { self.b.r_unless.as_ref().map(|o| &o[..]) } + fn val_names(&self) -> Option<&VecMap<&'e str>> { self.v.val_names.as_ref() } + fn is_set(&self, s: ArgSettings) -> bool { self.b.settings.is_set(s) } + fn set(&mut self, s: ArgSettings) { self.b.settings.set(s) } + fn has_switch(&self) -> bool { false } + fn max_vals(&self) -> Option { self.v.max_vals } + fn num_vals(&self) -> Option { self.v.num_vals } + fn possible_vals(&self) -> Option<&[&'e str]> { self.v.possible_vals.as_ref().map(|o| &o[..]) } fn validator(&self) -> Option<&Rc StdResult<(), String>>> { - self.validator.as_ref() - } - fn min_vals(&self) -> Option { - self.min_vals - } - fn short(&self) -> Option { - None - } - fn long(&self) -> Option<&'e str> { - None - } - fn val_delim(&self) -> Option { - self.val_delim - } - fn takes_value(&self) -> bool { - true - } - fn help(&self) -> Option<&'e str> { - self.help - } - fn default_val(&self) -> Option<&'n str> { - self.default_val - } - fn longest_filter(&self) -> bool { - true - } - fn aliases(&self) -> Option> { - None - } + self.v.validator.as_ref() + } + fn min_vals(&self) -> Option { self.v.min_vals } + fn short(&self) -> Option { None } + fn long(&self) -> Option<&'e str> { None } + fn val_delim(&self) -> Option { self.v.val_delim } + fn takes_value(&self) -> bool { true } + fn help(&self) -> Option<&'e str> { self.b.help } + fn default_val(&self) -> Option<&'n str> { self.v.default_val } + fn longest_filter(&self) -> bool { true } + fn aliases(&self) -> Option> { None } } impl<'n, 'e> DispOrder for PosBuilder<'n, 'e> { - fn disp_ord(&self) -> usize { - self.disp_ord - } + fn disp_ord(&self) -> usize { self.index as usize } } #[cfg(test)] diff --git a/src/args/arg_builder/switched.rs b/src/args/arg_builder/switched.rs new file mode 100644 index 00000000000..72af753aad6 --- /dev/null +++ b/src/args/arg_builder/switched.rs @@ -0,0 +1,46 @@ +use Arg; + +#[derive(Debug)] +pub struct Switched<'b> { + pub short: Option, + pub long: Option<&'b str>, + pub aliases: Option>, // (name, visible) + pub disp_ord: usize, + pub unified_ord: usize, +} + +impl<'e> Default for Switched<'e> { + fn default() -> Self { + Switched { + short: None, + long: None, + aliases: None, + disp_ord: 999, + unified_ord: 999, + } + } +} + +impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Switched<'e> { + fn from(a: &'z Arg<'n, 'e>) -> Self { + Switched { + short: a.short, + long: a.long, + aliases: a.aliases.clone(), + disp_ord: a.disp_ord, + .. Default::default() + } + } +} + +impl<'e> Clone for Switched<'e> { + fn clone(&self) -> Self { + Switched { + short: self.short, + long: self.long, + aliases: self.aliases.clone(), + disp_ord: self.disp_ord, + unified_ord: self.unified_ord, + } + } +} diff --git a/src/args/arg_builder/valued.rs b/src/args/arg_builder/valued.rs new file mode 100644 index 00000000000..e094e69ee3e --- /dev/null +++ b/src/args/arg_builder/valued.rs @@ -0,0 +1,60 @@ +use std::rc::Rc; + +use vec_map::VecMap; + +use Arg; + +#[allow(missing_debug_implementations)] +#[derive(Clone)] +pub struct Valued<'a, 'b> where 'a: 'b { + pub possible_vals: Option>, + pub val_names: Option>, + pub num_vals: Option, + pub max_vals: Option, + pub min_vals: Option, + pub validator: Option Result<(), String>>>, + pub val_delim: Option, + pub default_val: Option<&'a str>, +} + +impl<'n, 'e> Default for Valued<'n, 'e> { + fn default() -> Self { + Valued { + possible_vals: None, + num_vals: None, + min_vals: None, + max_vals: None, + val_names: None, + validator: None, + val_delim: Some(','), + default_val: None, + } + } +} + +impl<'n, 'e, 'z> From<&'z Arg<'n, 'e>> for Valued<'n, 'e> { + fn from(a: &'z Arg<'n, 'e>) -> Self { + let mut v = Valued { + possible_vals: a.possible_vals.clone(), + num_vals: a.num_vals, + min_vals: a.min_vals, + max_vals: a.max_vals, + val_names: a.val_names.clone(), + validator: a.validator.clone(), + val_delim: a.val_delim, + default_val: a.default_val, + .. Default::default() + }; + if let Some(ref vec) = a.val_names { + if vec.len() > 1 { + v.num_vals = Some(vec.len() as u64); + } + } + if let Some(ref vec) = a.val_names { + if vec.len() > 1 { + v.num_vals = Some(vec.len() as u64); + } + } + v + } +} diff --git a/src/args/mod.rs b/src/args/mod.rs index d0a128958ba..4e6f21114bb 100644 --- a/src/args/mod.rs +++ b/src/args/mod.rs @@ -1,11 +1,11 @@ pub use self::any_arg::{AnyArg, DispOrder}; pub use self::arg::Arg; -pub use self::arg_builder::{FlagBuilder, OptBuilder, PosBuilder}; +pub use self::arg_builder::{Base, Switched, Valued, FlagBuilder, OptBuilder, PosBuilder}; pub use self::arg_matcher::ArgMatcher; pub use self::arg_matches::{Values, OsValues, ArgMatches}; pub use self::group::ArgGroup; pub use self::matched_arg::MatchedArg; -pub use self::settings::ArgSettings; +pub use self::settings::{ArgFlags, ArgSettings}; pub use self::subcommand::SubCommand; #[macro_use] @@ -19,3 +19,13 @@ mod arg_builder; mod matched_arg; mod group; pub mod settings; + +#[doc(hidden)] +#[derive(Copy, Clone, Debug)] +pub enum ArgKind { + Flag, + Opt, + Pos, + Subcmd, + Group +} \ No newline at end of file diff --git a/src/args/settings.rs b/src/args/settings.rs index a8f7d8cf253..77f7bd66844 100644 --- a/src/args/settings.rs +++ b/src/args/settings.rs @@ -4,18 +4,19 @@ use std::str::FromStr; bitflags! { flags Flags: u16 { - const REQUIRED = 0b000000000001, - const MULTIPLE = 0b000000000010, - const EMPTY_VALS = 0b000000000100, - const GLOBAL = 0b000000001000, - const HIDDEN = 0b000000010000, - const TAKES_VAL = 0b000000100000, - const USE_DELIM = 0b000001000000, - const NEXT_LINE_HELP = 0b000010000000, - const R_UNLESS_ALL = 0b000100000000, - const REQ_DELIM = 0b001000000000, - const DELIM_NOT_SET = 0b010000000000, - const HIDE_POS_VALS = 0b100000000000, + const REQUIRED = 0b0000000000001, + const MULTIPLE = 0b0000000000010, + const EMPTY_VALS = 0b0000000000100, + const GLOBAL = 0b0000000001000, + const HIDDEN = 0b0000000010000, + const TAKES_VAL = 0b0000000100000, + const USE_DELIM = 0b0000001000000, + const NEXT_LINE_HELP = 0b0000010000000, + const R_UNLESS_ALL = 0b0000100000000, + const REQ_DELIM = 0b0001000000000, + const DELIM_NOT_SET = 0b0010000000000, + const HIDE_POS_VALS = 0b0100000000000, + const ALLOW_TAC_VALS = 0b1000000000000, } } @@ -40,7 +41,8 @@ impl ArgFlags { RequiredUnlessAll => R_UNLESS_ALL, RequireDelimiter => REQ_DELIM, ValueDelimiterNotSet => DELIM_NOT_SET, - HidePossibleValues => HIDE_POS_VALS + HidePossibleValues => HIDE_POS_VALS, + AllowLeadingHyphen => ALLOW_TAC_VALS } } @@ -78,6 +80,8 @@ pub enum ArgSettings { RequireDelimiter, /// Hides the possible values from the help string HidePossibleValues, + /// Allows vals that start with a '-' + AllowLeadingHyphen, #[doc(hidden)] RequiredUnlessAll, #[doc(hidden)] @@ -100,7 +104,44 @@ impl FromStr for ArgSettings { "requiredelimiter" => Ok(ArgSettings::RequireDelimiter), "valuedelimiternotset" => Ok(ArgSettings::ValueDelimiterNotSet), "hidepossiblevalues" => Ok(ArgSettings::HidePossibleValues), + "allowleadinghyphen" => Ok(ArgSettings::AllowLeadingHyphen), _ => Err("unknown ArgSetting, cannot convert from str".to_owned()), } } } + +#[cfg(test)] +mod test { + use super::ArgSettings; + + #[test] + fn arg_settings_fromstr() { + assert_eq!("allowleadinghyphen".parse::().unwrap(), + ArgSettings::AllowLeadingHyphen); + assert_eq!("emptyvalues".parse::().unwrap(), + ArgSettings::EmptyValues); + assert_eq!("global".parse::().unwrap(), + ArgSettings::Global); + assert_eq!("hidepossiblevalues".parse::().unwrap(), + ArgSettings::HidePossibleValues); + assert_eq!("hidden".parse::().unwrap(), + ArgSettings::Hidden); + assert_eq!("multiple".parse::().unwrap(), + ArgSettings::Multiple); + assert_eq!("nextlinehelp".parse::().unwrap(), + ArgSettings::NextLineHelp); + assert_eq!("requiredunlessall".parse::().unwrap(), + ArgSettings::RequiredUnlessAll); + assert_eq!("requiredelimiter".parse::().unwrap(), + ArgSettings::RequireDelimiter); + assert_eq!("required".parse::().unwrap(), + ArgSettings::Required); + assert_eq!("takesvalue".parse::().unwrap(), + ArgSettings::TakesValue); + assert_eq!("usevaluedelimiter".parse::().unwrap(), + ArgSettings::UseValueDelimiter); + assert_eq!("valuedelimiternotset".parse::().unwrap(), + ArgSettings::ValueDelimiterNotSet); + assert!("hahahaha".parse::().is_err()); + } +} \ No newline at end of file diff --git a/src/completions/bash.rs b/src/completions/bash.rs index 2db2735559a..42bc90853d1 100644 --- a/src/completions/bash.rs +++ b/src/completions/bash.rs @@ -141,8 +141,8 @@ complete -F _{name} {name} .p; } let mut opts = String::new(); - for o in &p.opts { - if let Some(l) = o.long { + for o in p.opts() { + if let Some(l) = o.s.long { opts = format!("{} --{}) COMPREPLY=({}) @@ -152,7 +152,7 @@ complete -F _{name} {name} l, self.vals_for(o)); } - if let Some(s) = o.short { + if let Some(s) = o.s.short { opts = format!("{} -{}) COMPREPLY=({}) diff --git a/src/completions/fish.rs b/src/completions/fish.rs index a83e2e969ae..69112300627 100644 --- a/src/completions/fish.rs +++ b/src/completions/fish.rs @@ -12,16 +12,13 @@ pub struct FishGen<'a, 'b> } impl<'a, 'b> FishGen<'a, 'b> { - pub fn new(p: &'b Parser<'a, 'b>) -> Self { - FishGen { p: p } - } + pub fn new(p: &'b Parser<'a, 'b>) -> Self { FishGen { p: p } } pub fn generate_to(&self, buf: &mut W) { let command = self.p.meta.bin_name.as_ref().unwrap(); // function to detect subcommand - let detect_subcommand_function = -r#"function __fish_using_command + let detect_subcommand_function = r#"function __fish_using_command set cmd (commandline -opc) if [ (count $cmd) -eq (count $argv) ] for i in (seq (count $argv)) @@ -34,7 +31,8 @@ r#"function __fish_using_command return 1 end -"#.to_string(); +"# + .to_string(); let mut buffer = detect_subcommand_function; gen_fish_inner(command, self, &command.to_string(), &mut buffer); @@ -42,10 +40,7 @@ end } } -fn gen_fish_inner(root_command: &str, - comp_gen: &FishGen, - parent_cmds: &str, - buffer: &mut String) { +fn gen_fish_inner(root_command: &str, comp_gen: &FishGen, parent_cmds: &str, buffer: &mut String) { // example : // // complete @@ -59,36 +54,36 @@ fn gen_fish_inner(root_command: &str, // -n "__fish_using_command myprog subcmd1" # complete for command "myprog subcmd1" let basic_template = format!("complete -c {} -n \"__fish_using_command {}\"", - root_command, - parent_cmds); + root_command, + parent_cmds); - for option in &comp_gen.p.opts { + for option in comp_gen.p.opts() { let mut template = basic_template.clone(); - if let Some(data) = option.short { + if let Some(data) = option.s.short { template.push_str(format!(" -s {}", data).as_str()); } - if let Some(data) = option.long { + if let Some(data) = option.s.long { template.push_str(format!(" -l {}", data).as_str()); } - if let Some(data) = option.help { + if let Some(data) = option.b.help { template.push_str(format!(" -d \"{}\"", data).as_str()); } - if let Some(ref data) = option.possible_vals { + if let Some(ref data) = option.v.possible_vals { template.push_str(format!(" -r -f -a \"{}\"", data.join(" ")).as_str()); } buffer.push_str(template.as_str()); buffer.push_str("\n"); } - for flag in &comp_gen.p.flags { + for flag in comp_gen.p.flags() { let mut template = basic_template.clone(); - if let Some(data) = flag.short { + if let Some(data) = flag.s.short { template.push_str(format!(" -s {}", data).as_str()); } - if let Some(data) = flag.long { + if let Some(data) = flag.s.long { template.push_str(format!(" -l {}", data).as_str()); } - if let Some(data) = flag.help { + if let Some(data) = flag.b.help { template.push_str(format!(" -d \"{}\"", data).as_str()); } buffer.push_str(template.as_str()); @@ -112,9 +107,6 @@ fn gen_fish_inner(root_command: &str, sub_parent_cmds.push_str(" "); } sub_parent_cmds.push_str(&subcommand.p.meta.name); - gen_fish_inner(root_command, - &sub_comp_gen, - &sub_parent_cmds, - buffer); + gen_fish_inner(root_command, &sub_comp_gen, &sub_parent_cmds, buffer); } } \ No newline at end of file diff --git a/src/completions/zsh.rs b/src/completions/zsh.rs index 576f1ce1a79..ba397bf40f7 100644 --- a/src/completions/zsh.rs +++ b/src/completions/zsh.rs @@ -152,8 +152,8 @@ fn subcommands_and_args_of(p: &Parser) -> String { for arg in p.positionals() { debugln!("iter;arg={}", arg.name); let a = format!("\"{name}:{help}\" \\", - name = arg.name.to_ascii_uppercase(), - help = arg.help.unwrap_or("")); + name = arg.b.name.to_ascii_uppercase(), + help = arg.b.help.unwrap_or("")); if !a.is_empty() { ret.push(a); diff --git a/tests/opts.rs b/tests/opts.rs index e9af6276c9c..f038ad45ded 100644 --- a/tests/opts.rs +++ b/tests/opts.rs @@ -225,6 +225,66 @@ fn require_delims() { assert_eq!(m.value_of("file").unwrap(), "some"); } +#[test] +fn leading_hyphen_pass() { + let r = App::new("mvae") + .arg( Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true)) + .get_matches_from_safe(vec!["", "-o", "-2", "3"]); + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.values_of("o").unwrap().collect::>(), &["-2", "3"]); +} + +#[test] +fn leading_hyphen_fail() { + let r = App::new("mvae") + .arg( Arg::from_usage("-o [opt] 'some opt'")) + .get_matches_from_safe(vec!["", "-o", "-2"]); + assert!(r.is_err()); + let m = r.unwrap_err(); + assert_eq!(m.kind, ErrorKind::UnknownArgument); +} + +#[test] +fn leading_hyphen_with_flag_after() { + let r = App::new("mvae") + .arg( Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true)) + .arg_from_usage("-f 'some flag'") + .get_matches_from_safe(vec!["", "-o", "-2", "-f"]); + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.values_of("o").unwrap().collect::>(), &["-2", "-f"]); + assert!(!m.is_present("f")); +} + +#[test] +fn leading_hyphen_with_flag_before() { + let r = App::new("mvae") + .arg( Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true)) + .arg_from_usage("-f 'some flag'") + .get_matches_from_safe(vec!["", "-f", "-o", "-2"]); + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.values_of("o").unwrap().collect::>(), &["-2"]); + assert!(m.is_present("f")); +} + +#[test] +fn leading_hyphen_with_only_pos_follows() { + let r = App::new("mvae") + .arg( Arg::from_usage("-o [opt]... 'some opt'").allow_hyphen_values(true)) + .arg_from_usage("[arg] 'some arg'") + .get_matches_from_safe(vec!["", "-o", "-2", "--", "val"]); + assert!(r.is_ok()); + let m = r.unwrap(); + assert!(m.is_present("o")); + assert_eq!(m.values_of("o").unwrap().collect::>(), &["-2"]); + assert_eq!(m.value_of("arg"), Some("val")); +} + #[test] #[cfg(feature="suggestions")] fn did_you_mean() {