From b01667ebb3a9dd1e7f81c80306d3a9e4ac52ce58 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Wed, 30 Sep 2015 23:07:28 -0400 Subject: [PATCH] refactor: changes some arg fields to flags internally --- src/app/app.rs | 125 +++++++++++++----------------- src/args/arg.rs | 2 +- src/args/argbuilder/flag.rs | 69 ++++++++--------- src/args/argbuilder/mod.rs | 3 + src/args/argbuilder/option.rs | 110 ++++++++++++-------------- src/args/argbuilder/positional.rs | 101 ++++++++++-------------- src/args/mod.rs | 2 + src/args/settings.rs | 74 ++++++++++++++++++ src/macros.rs | 1 + 9 files changed, 257 insertions(+), 230 deletions(-) create mode 100644 src/args/settings.rs diff --git a/src/app/app.rs b/src/app/app.rs index c7dcac34..b7107a16 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -12,6 +12,7 @@ use yaml_rust::Yaml; use args::{ArgMatches, Arg, SubCommand, MatchedArg}; use args::{FlagBuilder, OptBuilder, PosBuilder}; +use args::settings::{ArgSettings, ArgFlags}; use args::ArgGroup; use fmt::Format; use super::settings::{AppSettings, AppFlags}; @@ -790,7 +791,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } if let Some(l) = a.long { - // self.long_list.dedup(); if self.long_list.contains(&l) { panic!("Argument long must be unique\n\n\t--{} is already in use", l); } else { @@ -1084,7 +1084,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } } - res.dedup(); if res.is_empty() { return None } Some(res) @@ -1112,7 +1111,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } - g_vec.dedup(); if !g_vec.is_empty() { for av in g_vec.iter().map(|g| self.get_group_members(g)) { for a in av { @@ -1145,7 +1143,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } - g_vec.dedup(); if !g_vec.is_empty() { for av in g_vec.iter().map(|g| self.get_group_members_names(g)) { for a in av { @@ -1159,10 +1156,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } fn get_required_from(&self, - mut reqs: Vec<&'ar str>, + reqs: Vec<&'ar str>, matches: Option<&ArgMatches>) -> VecDeque { - reqs.dedup(); let mut c_flags = vec![]; let mut c_pos = vec![]; let mut c_opt = vec![]; @@ -1318,7 +1314,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ reqs.push(p.name); continue; } - if p.required { + if p.settings.is_set(&ArgSettings::Required) { found = true; reqs.push(p.name); } @@ -1336,19 +1332,19 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ usage.push_str(" [OPTIONS]"); } if !self.settings.is_set(&AppSettings::UnifiedHelpMessage) && !self.opts.is_empty() && - self.opts.values().any(|a| !a.required) { + self.opts.values().any(|a| !a.settings.is_set(&ArgSettings::Required)) { usage.push_str(" [OPTIONS]"); } // places a '--' in the usage string if there are args and options // supporting multiple values - if !self.positionals_idx.is_empty() && self.opts.values().any(|a| a.multiple ) && - !self.opts.values().any(|a| a.required) && + if !self.positionals_idx.is_empty() && self.opts.values().any(|a| a.settings.is_set(&ArgSettings::Multiple)) && + !self.opts.values().any(|a| a.settings.is_set(&ArgSettings::Required)) && self.subcommands.is_empty() { usage.push_str(" [--]") } if !self.positionals_idx.is_empty() && self.positionals_idx.values() - .any(|a| !a.required) { + .any(|a| !a.settings.is_set(&ArgSettings::Required)) { usage.push_str(" [ARGS]"); } @@ -1412,7 +1408,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ let mut longest_flag = 0; for fl in self.flags .values() - .filter(|f| f.long.is_some() && !f.hidden) + .filter(|f| f.long.is_some() && !f.settings.is_set(&ArgSettings::Hidden)) // 2='--' .map(|a| a.to_string().len() ) { if fl > longest_flag { @@ -1422,7 +1418,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ let mut longest_opt = 0; for ol in self.opts .values() - .filter(|o| !o.hidden) + .filter(|o| !o.settings.is_set(&ArgSettings::Hidden)) .map(|a| a.to_string().len() ) { @@ -1433,7 +1429,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ let mut longest_pos = 0; for pl in self.positionals_idx .values() - .filter(|p| !p.hidden) + .filter(|p| !p.settings.is_set(&ArgSettings::Hidden)) .map(|f| f.to_string().len() ) { if pl > longest_pos { longest_pos = pl; @@ -1471,7 +1467,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } for v in self.flags .values() - .filter(|f| !f.hidden) { + .filter(|f| !f.settings.is_set(&ArgSettings::Hidden)) { try!(write!(w, "{}", tab)); if let Some(s) = v.short { try!(write!(w, "-{}",s)); @@ -1530,7 +1526,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } for v in self.opts .values() - .filter(|o| !o.hidden) { + .filter(|o| !o.settings.is_set(&ArgSettings::Hidden)) { // if it supports multiple we add '...' i.e. 3 to the name length try!(write!(w, "{}", tab)); if let Some(s) = v.short { @@ -1550,7 +1546,13 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ try!(write!(w, " <{}>", v.name)); } } else { - try!(write!(w, " <{}>{}", v.name, if v.multiple{"..."} else {""})); + try!(write!(w, " <{}>{}", v.name, + if v.settings.is_set(&ArgSettings::Multiple) { + "..." + } else { + "" + } + )); } if v.long.is_some() { try!(self.print_spaces( @@ -1568,11 +1570,10 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ try!(write!(w, "\nARGS:\n")); for v in self.positionals_idx .values() - .filter(|p| !p.hidden) { - // let mult = if v.multiple { 3 } else { 0 }; + .filter(|p| !p.settings.is_set(&ArgSettings::Hidden)) { try!(write!(w, "{}", tab)); try!(write!(w, "{}", v.name)); - if v.multiple { + if v.settings.is_set(&ArgSettings::Multiple) { try!(write!(w, "...")); } try!(self.print_spaces((longest_pos + 4) - (v.to_string().len()), w)); @@ -2024,7 +2025,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } if let Some(ref p) = self.positionals_idx.values() - .filter(|a| a.multiple) + .filter(|a| a.settings.is_set(&ArgSettings::Multiple)) .filter(|a| { a.index as usize != self.positionals_idx.len() }) @@ -2037,11 +2038,11 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ let mut found = false; for (_, p) in self.positionals_idx.iter_mut().rev() { if found { - p.required = true; + p.settings.set(&ArgSettings::Required); self.required.push(p.name); continue; } - if p.required { + if p.settings.is_set(&ArgSettings::Required) { found = true; } } @@ -2152,7 +2153,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if let Some(num) = opt.num_vals { if let Some(ref ma) = matches.args.get(opt.name) { if let Some(ref vals) = ma.values { - if num == vals.len() as u8 && !opt.multiple { + if num == vals.len() as u8 && !opt.settings.is_set(&ArgSettings::Multiple) { return Err(self.report_error(format!("The argument '{}' \ was found, but '{}' only expects {} values", Format::Warning(arg_slice), @@ -2166,7 +2167,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } // if it's an empty value, and we don't allow that, report the error - if !opt.empty_vals && matches.args.contains_key(opt.name) && + if !opt.settings.is_set(&ArgSettings::EmptyValues) && matches.args.contains_key(opt.name) && arg_slice.is_empty() { return Err(self.report_error( format!("The argument '{}' does not allow empty values, but one \ @@ -2178,7 +2179,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if let Some(ref vec) = self.groups_for(opt.name) { for grp in vec { if let Some(ref mut o) = matches.args.get_mut(grp) { - o.occurrences = if opt.multiple { + o.occurrences = if opt.settings.is_set(&ArgSettings::Multiple) { o.occurrences + 1 } else { 1 @@ -2196,7 +2197,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if let Some(ref mut o) = matches.args.get_mut(opt.name) { // if it's multiple; the occurrences are increased when originally // found - o.occurrences = if opt.multiple { + o.occurrences = if opt.settings.is_set(&ArgSettings::Multiple) { o.occurrences + 1 } else { skip = true; @@ -2232,7 +2233,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ continue } } else if let Some(num) = opt.num_vals { - if opt.multiple { + if opt.settings.is_set(&ArgSettings::Multiple) { val_counter += 1; if val_counter != num { continue @@ -2263,7 +2264,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } else if let Some(ref name) = needs_val_of { // We've reached more values for an option than it possibly accepts if let Some(ref o) = self.opts.get(name) { - if !o.multiple { + if !o.settings.is_set(&ArgSettings::Multiple) { return Err(self.report_error( format!("The argument '{}' requires a value but none was supplied", Format::Warning(o.to_string())), @@ -2340,12 +2341,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ Some(matches.args.keys().map(|k| *k).collect()))); } else if let Some(p) = self.positionals_idx.get(&pos_counter) { // Make sure this one doesn't conflict with anything - self.blacklist.dedup(); if self.blacklist.contains(&p.name) { - // we shouldn't need to remove this arg...since it should be matched yet - // anyways - // matches.args.remove(p.name); - return Err(self.report_error(format!("The argument '{}' cannot be used \ with {}", Format::Warning(p.to_string()), @@ -2369,7 +2365,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ // Have we made the update yet? let mut done = false; - if p.multiple { + if p.settings.is_set(&ArgSettings::Multiple) { if let Some(num) = p.num_vals { if let Some(ref ma) = matches.args.get(p.name) { if let Some(ref vals) = ma.values { @@ -2384,7 +2380,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } } - if !p.empty_vals && matches.args.contains_key(p.name) && + if !p.settings.is_set(&ArgSettings::EmptyValues) && matches.args.contains_key(p.name) && arg_slice.is_empty() { return Err(self.report_error(format!("The argument '{}' does not \ allow empty values, but one was found.", @@ -2407,7 +2403,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } // Was an update made, or is this the first occurrence? if !done { - self.overrides.dedup(); if self.overrides.contains(&p.name) { if let Some(name) = self.overriden_from(p.name, matches) { matches.args.remove(&*name); @@ -2422,7 +2417,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } } let mut bm = BTreeMap::new(); - if !p.empty_vals && arg_slice.is_empty() { + if !p.settings.is_set(&ArgSettings::EmptyValues) && arg_slice.is_empty() { return Err(self.report_error(format!("The argument '{}' does not \ allow empty values, but one was found.", Format::Warning(p.to_string())), @@ -2483,7 +2478,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } if let Some(ref a) = needs_val_of { if let Some(o) = self.opts.get(a) { - if o.multiple && self.required.is_empty() { + if o.settings.is_set(&ArgSettings::Multiple) && self.required.is_empty() { let should_err = match matches.values_of(o.name) { Some(ref v) => v.is_empty(), None => true, @@ -2495,7 +2490,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ ClapErrorType::EmptyValue, Some(matches.args.keys().map(|k| *k).collect()))); } - } else if !o.multiple { + } else if !o.settings.is_set(&ArgSettings::Multiple) { return Err(self.report_error( format!("The argument '{}' requires a value but none was supplied", Format::Warning(o.to_string())), @@ -2517,10 +2512,10 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .map(|k| *k) .filter(|k| { if let Some(o) = self.opts.get(k) { - !o.required + !o.settings.is_set(&ArgSettings::Required) } else if let Some(p) = self.positionals_name.get(k) { if let Some(p) = self.positionals_idx.get(p) { - !p.required + !p.settings.is_set(&ArgSettings::Required) } else { true } @@ -2632,10 +2627,10 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .map(|k| *k) .filter(|k| { if let Some(o) = self.opts.get(k) { - !o.required + !o.settings.is_set(&ArgSettings::Required) } else if let Some(p) = self.positionals_name.get(k) { if let Some(p) = self.positionals_idx.get(p) { - !p.required + !p.settings.is_set(&ArgSettings::Required) } else { true } @@ -2740,11 +2735,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ long: Some("help"), help: Some("Prints help information"), blacklist: None, - multiple: false, - global: false, requires: None, overrides: None, - hidden: false, + settings: ArgFlags::new() }; self.long_list.push("help"); self.flags.insert("hclap_help", arg); @@ -2763,11 +2756,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ long: Some("version"), help: Some("Prints version information"), blacklist: None, - multiple: false, - global: false, requires: None, overrides: None, - hidden: false, + settings: ArgFlags::new() }; self.long_list.push("version"); self.flags.insert("vclap_version", arg); @@ -2838,7 +2829,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .filter(|&v| v.long.is_some()) .filter(|&v| v.long.unwrap() == arg).nth(0) { // prevents "--config= value" typo - if arg_vec[1].len() == 0 && !v.empty_vals { + if arg_vec[1].len() == 0 && !v.settings.is_set(&ArgSettings::EmptyValues) { if let Some(ref vec) = self.groups_for(v.name) { for grp in vec { matches.args.insert(grp, MatchedArg{ @@ -2872,8 +2863,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ ClapErrorType::ArgumentConflict, Some(matches.args.keys().map(|k| *k).collect()))); } - self.overrides.dedup(); - debugln!("checking if {} is in overrides", v.name); if self.overrides.contains(&v.name) { debugln!("it is..."); debugln!("checking who defined it..."); @@ -2902,7 +2891,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } if matches.args.contains_key(v.name) { - if !v.multiple { + if !v.settings.is_set(&ArgSettings::Multiple) { return Err(self.report_error(format!("The argument '{}' was supplied more \ than once, but does not support multiple values", Format::Warning(format!("--{}", arg))), @@ -3006,7 +2995,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .filter(|&v| v.long.is_some()) .filter(|&v| v.long.unwrap() == arg).nth(0) { // Ensure this flag isn't on the mutually excludes list - self.blacklist.dedup(); if self.blacklist.contains(&v.name) { matches.args.remove(v.name); return Err(self.report_error(format!("The argument '{}' cannot be used with {}", @@ -3018,8 +3006,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ ClapErrorType::ArgumentConflict, Some(matches.args.keys().map(|k| *k).collect()))); } - self.overrides.dedup(); - debugln!("checking if {} is in overrides", v.name); if self.overrides.contains(&v.name) { debugln!("it is..."); debugln!("checking who defined it..."); @@ -3038,7 +3024,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } // Make sure this isn't one being added multiple times if it doesn't suppor it - if matches.args.contains_key(v.name) && !v.multiple { + if matches.args.contains_key(v.name) && !v.settings.is_set(&ArgSettings::Multiple) { return Err(self.report_error(format!("The argument '{}' was supplied more than \ once, but does not support multiple values", Format::Warning(v.to_string())), @@ -3050,7 +3036,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ done = false; if let Some(ref mut f) = matches.args.get_mut(v.name) { done = true; - f.occurrences = if v.multiple { + f.occurrences = if v.settings.is_set(&ArgSettings::Multiple) { f.occurrences + 1 } else { 1 @@ -3169,7 +3155,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ return Err(self.possible_values_error(av, &v.to_string(), p_vals, matches)); } } - if !v.empty_vals && av.is_empty() && matches.args.contains_key(v.name) { + if !v.settings.is_set(&ArgSettings::EmptyValues) && av.is_empty() && matches.args.contains_key(v.name) { return Err(self.report_error(format!("The argument '{}' does not allow empty \ values, but one was found.", Format::Warning(v.to_string())), ClapErrorType::EmptyValue, @@ -3244,7 +3230,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .filter(|&v| v.short.is_some()) .filter(|&v| v.short.unwrap() == arg_c).nth(0) { // Ensure this option isn't on the master mutually excludes list - self.blacklist.dedup(); if self.blacklist.contains(&v.name) { matches.args.remove(v.name); return Err(self.report_error(format!("The argument '{}' cannot be used with {}", @@ -3256,7 +3241,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ ClapErrorType::ArgumentConflict, Some(matches.args.keys().map(|k| *k).collect()))); } - self.overrides.dedup(); if self.overrides.contains(&v.name) { if let Some(name) = self.overriden_from(v.name, matches) { matches.args.remove(&*name); @@ -3272,7 +3256,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } if matches.args.contains_key(v.name) { - if !v.multiple { + if !v.settings.is_set(&ArgSettings::Multiple) { return Err(self.report_error(format!("The argument '{}' was supplied more \ than once, but does not support multiple values", Format::Warning(format!("-{}", arg))), @@ -3334,7 +3318,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ .filter(|&v| v.short.is_some()) .filter(|&v| v.short.unwrap() == arg).nth(0) { // Ensure this flag isn't on the mutually excludes list - self.blacklist.dedup(); if self.blacklist.contains(&v.name) { matches.args.remove(v.name); return Err(self.report_error(format!("The argument '{}' cannot be used {}", @@ -3347,8 +3330,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ ClapErrorType::ArgumentConflict, Some(matches.args.keys().map(|k| *k).collect()))); } - self.overrides.dedup(); - debugln!("checking if {} is in overrides", v.name); if self.overrides.contains(&v.name) { debugln!("it is..."); debugln!("checking who defined it..."); @@ -3367,7 +3348,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } // Make sure this isn't one being added multiple times if it doesn't suppor it - if matches.args.contains_key(v.name) && !v.multiple { + if matches.args.contains_key(v.name) && !v.settings.is_set(&ArgSettings::Multiple) { return Err(self.report_error(format!("The argument '{}' was supplied more than \ once, but does not support multiple values", Format::Warning(format!("-{}", arg))), @@ -3378,7 +3359,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ if let Some(ref vec) = self.groups_for(v.name) { for grp in vec { if let Some(ref mut f) = matches.args.get_mut(grp) { - f.occurrences = if v.multiple { + f.occurrences = if v.settings.is_set(&ArgSettings::Multiple) { f.occurrences + 1 } else { 1 @@ -3389,7 +3370,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ let mut done = false; if let Some(ref mut f) = matches.args.get_mut(v.name) { done = true; - f.occurrences = if v.multiple { + f.occurrences = if v.settings.is_set(&ArgSettings::Multiple) { f.occurrences + 1 } else { 1 @@ -3495,7 +3476,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ } else if let Some(ref vals) = ma.values { if let Some(f) = self.opts.get(name) { if let Some(num) = f.num_vals { - let should_err = if f.multiple { + let should_err = if f.settings.is_set(&ArgSettings::Multiple) { ((vals.len() as u8) % num) != 0 } else { num != (vals.len() as u8) @@ -3505,13 +3486,13 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ values, but {} w{} provided", Format::Warning(f.to_string()), Format::Good(num.to_string()), - Format::Error(if f.multiple { + Format::Error(if f.settings.is_set(&ArgSettings::Multiple) { (vals.len() % num as usize).to_string() } else { vals.len().to_string() }), if vals.len() == 1 || - ( f.multiple && + ( f.settings.is_set(&ArgSettings::Multiple) && ( vals.len() % num as usize) == 1) {"as"}else{"ere"}), ClapErrorType::EmptyValue, Some(matches.args.keys().map(|k| *k).collect()))); diff --git a/src/args/arg.rs b/src/args/arg.rs index 94c63ae4..46a8190f 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -85,7 +85,7 @@ pub struct Arg<'n, 'l, 'h, 'g, 'p, 'r> { pub min_vals: Option, /// Specifies whether or not this argument accepts explicit empty values such as `--option ""` pub empty_vals: bool, - /// Specifies whether or not this argument is global and should be propogated through all + /// Specifies whether or not this argument is global and should be propagated through all /// child subcommands pub global: bool, /// A function used to check the validity of an argument value. Failing this validation results diff --git a/src/args/argbuilder/flag.rs b/src/args/argbuilder/flag.rs index e1835e55..8ba5e0c2 100644 --- a/src/args/argbuilder/flag.rs +++ b/src/args/argbuilder/flag.rs @@ -3,6 +3,7 @@ use std::fmt::{Display, Formatter, Result}; use std::convert::From; use Arg; +use args::settings::{ArgFlags, ArgSettings}; pub struct FlagBuilder<'n> { pub name: &'n str, @@ -13,10 +14,6 @@ pub struct FlagBuilder<'n> { /// the user when the application's `help` /// text is displayed pub help: Option<&'n str>, - /// Determines if multiple instances of the same - /// flag are allowed - /// I.e. `-v -v -v` or `-vvv` - pub multiple: bool, /// A list of names for other arguments that /// *may not* be used with this flag pub blacklist: Option>, @@ -27,10 +24,24 @@ pub struct FlagBuilder<'n> { /// The short version (i.e. single character) /// of the argument, no preceding `-` pub short: Option, - pub global: bool, /// A list of names for other arguments that *mutually override* this flag pub overrides: Option>, - pub hidden: bool + pub settings: ArgFlags, +} + +impl<'n> FlagBuilder<'n> { + pub fn new(name: &'n str) -> Self { + FlagBuilder { + name: name, + short: None, + long: None, + help: None, + blacklist: None, + requires: None, + overrides: None, + settings: ArgFlags::new() + } + } } impl<'n, 'a> From<&'a Arg<'n, 'n, 'n, 'n, 'n, 'n>> for FlagBuilder<'n> { @@ -60,12 +71,19 @@ impl<'n, 'a> From<&'a Arg<'n, 'n, 'n, 'n, 'n, 'n>> for FlagBuilder<'n> { long: a.long, help: a.help, blacklist: None, - global: a.global, - multiple: a.multiple, requires: None, overrides: None, - hidden: a.hidden + settings: ArgFlags::new() }; + if a.multiple { + fb.settings.set(&ArgSettings::Multiple); + } + if a.global { + fb.settings.set(&ArgSettings::Global); + } + if a.hidden { + fb.settings.set(&ArgSettings::Hidden); + } // Check if there is anything in the blacklist (mutually excludes list) and add any // values if let Some(ref bl) = a.blacklist { @@ -74,7 +92,6 @@ impl<'n, 'a> From<&'a Arg<'n, 'n, 'n, 'n, 'n, 'n>> for FlagBuilder<'n> { for n in bl { bhs.push(*n); } - bhs.dedup(); fb.blacklist = Some(bhs); } // Check if there is anything in the requires list and add any values @@ -84,7 +101,6 @@ impl<'n, 'a> From<&'a Arg<'n, 'n, 'n, 'n, 'n, 'n>> for FlagBuilder<'n> { for n in r { rhs.push(*n); } - rhs.dedup(); fb.requires = Some(rhs); } if let Some(ref or) = a.overrides { @@ -93,7 +109,6 @@ impl<'n, 'a> From<&'a Arg<'n, 'n, 'n, 'n, 'n, 'n>> for FlagBuilder<'n> { for n in or { bhs.push(*n); } - bhs.dedup(); fb.overrides = Some(bhs); } @@ -115,36 +130,18 @@ impl<'n> Display for FlagBuilder<'n> { #[cfg(test)] mod test { use super::FlagBuilder; + use args::settings::ArgSettings; #[test] fn flagbuilder_display() { - let f = FlagBuilder { - name: "flg", - short: None, - long: Some("flag"), - help: None, - multiple: true, - blacklist: None, - requires: None, - global: false, - overrides: None, - hidden: false, - }; + let mut f = FlagBuilder::new("flg"); + f.settings.set(&ArgSettings::Multiple); + f.long = Some("flag"); assert_eq!(&*format!("{}", f), "--flag"); - let f2 = FlagBuilder { - name: "flg", - short: Some('f'), - long: None, - help: None, - multiple: false, - blacklist: None, - requires: None, - global: false, - overrides: None, - hidden: false, - }; + let mut f2 = FlagBuilder::new("flg"); + f2.short = Some('f'); assert_eq!(&*format!("{}", f2), "-f"); } diff --git a/src/args/argbuilder/mod.rs b/src/args/argbuilder/mod.rs index 53ccc9e7..2f96a5c7 100644 --- a/src/args/argbuilder/mod.rs +++ b/src/args/argbuilder/mod.rs @@ -2,6 +2,9 @@ pub use self::flag::FlagBuilder; pub use self::option::OptBuilder; pub use self::positional::PosBuilder; +#[allow(dead_code)] mod flag; +#[allow(dead_code)] mod positional; +#[allow(dead_code)] mod option; diff --git a/src/args/argbuilder/option.rs b/src/args/argbuilder/option.rs index 71b8cff0..0ce270e9 100644 --- a/src/args/argbuilder/option.rs +++ b/src/args/argbuilder/option.rs @@ -4,6 +4,7 @@ use std::fmt::{Display, Formatter, Result}; use std::result::Result as StdResult; use Arg; +use args::settings::{ArgFlags, ArgSettings}; pub struct OptBuilder<'n> { pub name: &'n str, @@ -14,15 +15,8 @@ pub struct OptBuilder<'n> { /// The string of text that will displayed to the user when the application's /// `help` text is displayed pub help: Option<&'n str>, - /// Allow multiple occurrences of an option argument such as "-c some -c other" - pub multiple: bool, /// A list of names for other arguments that *may not* be used with this flag pub blacklist: Option>, - /// If this is a required by default when using the command line program - /// i.e. a configuration file that's required for the program to function - /// **NOTE:** required by default means, it is required *until* mutually - /// exclusive arguments are evaluated. - pub required: bool, /// A list of possible values for this argument pub possible_vals: Option>, /// A list of names of other arguments that are *required* to be used when @@ -32,15 +26,32 @@ pub struct OptBuilder<'n> { pub min_vals: Option, pub max_vals: Option, pub val_names: Option>, - pub empty_vals: bool, - pub global: bool, pub validator: Option StdResult<(), String>>>, /// A list of names for other arguments that *mutually override* this flag pub overrides: Option>, - pub hidden: bool + pub settings: ArgFlags, } impl<'n> OptBuilder<'n> { + pub fn new(name: &'n str) -> Self { + OptBuilder { + name: name, + short: None, + long: 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() + } + } + pub fn from_arg(a: &Arg<'n, 'n, 'n, 'n, 'n,'n>, reqs: &mut Vec<&'n str>) -> Self { if a.short.is_none() && a.long.is_none() { @@ -52,30 +63,41 @@ impl<'n> OptBuilder<'n> { name: a.name, short: a.short, long: a.long, - multiple: a.multiple, blacklist: None, help: a.help, - global: a.global, possible_vals: None, num_vals: a.num_vals, min_vals: a.min_vals, max_vals: a.max_vals, val_names: a.val_names.clone(), requires: None, - required: a.required, - empty_vals: a.empty_vals, validator: None, overrides: None, - hidden: a.hidden + settings: ArgFlags::new() }; + if a.multiple { + ob.settings.set(&ArgSettings::Multiple); + } + if a.required { + ob.settings.set(&ArgSettings::Required); + } + if a.global { + ob.settings.set(&ArgSettings::Global); + } + if !a.empty_vals { + ob.settings.unset(&ArgSettings::Global); + } + if a.hidden { + ob.settings.set(&ArgSettings::Hidden); + } if let Some(ref vec) = ob.val_names { ob.num_vals = Some(vec.len() as u8); } - if ob.min_vals.is_some() && !ob.multiple { + if ob.min_vals.is_some() && !a.multiple { panic!("Argument \"{}\" does not allow multiple values, yet it is expecting {} \ values", ob.name, ob.num_vals.unwrap()); } - if ob.max_vals.is_some() && !ob.multiple { + if ob.max_vals.is_some() && !a.multiple { panic!("Argument \"{}\" does not allow multiple values, yet it is expecting {} \ values", ob.name, ob.num_vals.unwrap()); } @@ -87,7 +109,6 @@ impl<'n> OptBuilder<'n> { for n in bl { bhs.push(*n); } - bhs.dedup(); ob.blacklist = Some(bhs); } if let Some(ref p) = a.validator { @@ -99,11 +120,10 @@ impl<'n> OptBuilder<'n> { // without derefing n = &&str for n in r { rhs.push(*n); - if ob.required { + if a.required { reqs.push(*n); } } - rhs.dedup(); ob.requires = Some(rhs); } if let Some(ref or) = a.overrides { @@ -112,7 +132,6 @@ impl<'n> OptBuilder<'n> { for n in or { bhs.push(*n); } - bhs.dedup(); ob.overrides = Some(bhs); } // Check if there is anything in the possible values and add those as well @@ -150,7 +169,7 @@ impl<'n> Display for OptBuilder<'n> { for _ in (0..num) { try!(write!(f, " <{}>", self.name)); } - if self.multiple && num == 1 { + if self.settings.is_set(&ArgSettings::Multiple) && num == 1 { try!(write!(f, "...")); } } @@ -163,29 +182,13 @@ impl<'n> Display for OptBuilder<'n> { mod test { use super::OptBuilder; use std::collections::BTreeSet; + use args::settings::ArgSettings; #[test] fn optbuilder_display() { - let o = OptBuilder { - name: "opt", - short: None, - long: Some("option"), - help: None, - multiple: true, - blacklist: None, - required: false, - possible_vals: None, - requires: None, - num_vals: None, - min_vals: None, - max_vals: None, - val_names: None, - empty_vals: true, - global: false, - validator: None, - overrides: None, - hidden: false, - }; + let mut o = OptBuilder::new("opt"); + o.long = Some("option"); + o.settings.set(&ArgSettings::Multiple); assert_eq!(&*format!("{}", o), "--option ..."); @@ -193,26 +196,9 @@ mod test { v_names.insert("file"); v_names.insert("name"); - let o2 = OptBuilder { - name: "opt", - short: Some('o'), - long: None, - help: None, - multiple: false, - blacklist: None, - required: false, - possible_vals: None, - requires: None, - num_vals: None, - min_vals: None, - max_vals: None, - val_names: Some(v_names), - empty_vals: true, - global: false, - validator: None, - overrides: None, - hidden: false, - }; + let mut o2 = OptBuilder::new("opt"); + o2.short = Some('o'); + o2.val_names = Some(v_names); assert_eq!(&*format!("{}", o2), "-o "); } diff --git a/src/args/argbuilder/positional.rs b/src/args/argbuilder/positional.rs index d227cfaa..039a90c3 100644 --- a/src/args/argbuilder/positional.rs +++ b/src/args/argbuilder/positional.rs @@ -3,19 +3,13 @@ use std::result::Result as StdResult; use std::rc::Rc; use Arg; +use args::settings::{ArgFlags, ArgSettings}; pub struct PosBuilder<'n> { pub name: &'n str, /// The string of text that will displayed to the user when the application's /// `help` text is displayed pub help: Option<&'n str>, - /// If this is a required by default when using the command line program - /// i.e. a configuration file that's required for the program to function - /// **NOTE:** required by default means, it is required *until* mutually - /// exclusive arguments are evaluated. - pub required: bool, - /// Allow multiple occurrences of an option argument such as "-c some -c other" - pub multiple: bool, /// A list of names of other arguments that are *required* to be used when /// this flag is used pub requires: Option>, @@ -28,15 +22,30 @@ pub struct PosBuilder<'n> { pub num_vals: Option, pub max_vals: Option, pub min_vals: Option, - pub empty_vals: bool, - pub global: bool, pub validator: Option StdResult<(), String>>>, /// A list of names for other arguments that *mutually override* this flag pub overrides: Option>, - pub hidden: bool + pub settings: ArgFlags, } impl<'n> PosBuilder<'n> { + pub fn new(name: &'n str, idx: u8) -> Self { + PosBuilder { + name: name, + index: idx, + help: None, + blacklist: None, + possible_vals: None, + requires: None, + num_vals: None, + min_vals: None, + max_vals: None, + validator: None, + overrides: None, + settings: ArgFlags::new() + } + } + pub fn from_arg(a: &Arg<'n, 'n, 'n, 'n, 'n, 'n>, idx: u8, reqs: &mut Vec<&'n str>) -> Self { @@ -61,8 +70,6 @@ impl<'n> PosBuilder<'n> { let mut pb = PosBuilder { name: a.name, index: idx, - required: a.required, - multiple: a.multiple, blacklist: None, requires: None, possible_vals: None, @@ -70,17 +77,27 @@ impl<'n> PosBuilder<'n> { min_vals: a.min_vals, max_vals: a.max_vals, help: a.help, - global: a.global, - empty_vals: a.empty_vals, validator: None, overrides: None, - hidden: a.hidden + settings: ArgFlags::new() }; - if pb.min_vals.is_some() && !pb.multiple { + if a.multiple { + pb.settings.set(&ArgSettings::Multiple); + } + if a.required { + pb.settings.set(&ArgSettings::Required); + } + if a.global { + pb.settings.set(&ArgSettings::Global); + } + if a.hidden { + pb.settings.set(&ArgSettings::Hidden); + } + if pb.min_vals.is_some() && !a.multiple { panic!("Argument \"{}\" does not allow multiple values, yet it is expecting {} \ values", pb.name, pb.num_vals.unwrap()); } - if pb.max_vals.is_some() && !pb.multiple { + if pb.max_vals.is_some() && !a.multiple { panic!("Argument \"{}\" does not allow multiple values, yet it is expecting {} \ values", pb.name, pb.num_vals.unwrap()); } @@ -92,7 +109,6 @@ impl<'n> PosBuilder<'n> { for n in bl { bhs.push(*n); } - bhs.dedup(); pb.blacklist = Some(bhs); } if let Some(ref or) = a.overrides { @@ -101,7 +117,6 @@ impl<'n> PosBuilder<'n> { for n in or { bhs.push(*n); } - bhs.dedup(); pb.overrides = Some(bhs); } // Check if there is anything in the possible values and add those as well @@ -122,11 +137,10 @@ impl<'n> PosBuilder<'n> { // without derefing n = &&str for n in r { rhs.push(*n); - if pb.required { + if a.required { reqs.push(*n); } } - rhs.dedup(); pb.requires = Some(rhs); } @@ -138,12 +152,12 @@ impl<'n> Display for PosBuilder<'n> { fn fmt(&self, f: &mut Formatter) -> Result { - if self.required { + if self.settings.is_set(&ArgSettings::Required) { try!(write!(f, "<{}>", self.name)); } else { try!(write!(f, "[{}]", self.name)); } - if self.multiple { + if self.settings.is_set(&ArgSettings::Multiple) { try!(write!(f, "...")); } @@ -153,48 +167,17 @@ impl<'n> Display for PosBuilder<'n> { #[cfg(test)] mod test { use super::PosBuilder; + use args::settings::ArgSettings; #[test] fn posbuilder_display() { - let p = PosBuilder { - name: "pos", - help: None, - multiple: true, - blacklist: None, - required: false, - possible_vals: None, - requires: None, - num_vals: None, - min_vals: None, - max_vals: None, - index: 1, - empty_vals: true, - global: false, - validator: None, - overrides: None, - hidden: false, - }; + let mut p = PosBuilder::new("pos", 1); + p.settings.set(&ArgSettings::Multiple); assert_eq!(&*format!("{}", p), "[pos]..."); - let p2 = PosBuilder { - name: "pos", - help: None, - multiple: false, - blacklist: None, - required: true, - possible_vals: None, - requires: None, - num_vals: None, - min_vals: None, - max_vals: None, - index: 1, - empty_vals: true, - global: false, - validator: None, - overrides: None, - hidden: false, - }; + let mut p2 = PosBuilder::new("pos", 1); + p2.settings.set(&ArgSettings::Required); assert_eq!(&*format!("{}", p2), ""); } diff --git a/src/args/mod.rs b/src/args/mod.rs index 8d61cf04..9b3bd5f8 100644 --- a/src/args/mod.rs +++ b/src/args/mod.rs @@ -11,3 +11,5 @@ mod subcommand; mod argbuilder; mod matchedarg; mod group; +#[allow(dead_code)] +pub mod settings; \ No newline at end of file diff --git a/src/args/settings.rs b/src/args/settings.rs new file mode 100644 index 00000000..af3ac141 --- /dev/null +++ b/src/args/settings.rs @@ -0,0 +1,74 @@ +use std::str::FromStr; +use std::ascii::AsciiExt; + +bitflags! { + flags Flags: u8 { + const REQUIRED = 0b000001, + const MULTIPLE = 0b000100, + const EMPTY_VALS = 0b001000, + const GLOBAL = 0b010000, + const HIDDEN = 0b100000, + } +} + +pub struct ArgFlags(Flags); + +impl ArgFlags { + pub fn new() -> Self { + ArgFlags(EMPTY_VALS) + } + + pub fn set(&mut self, s: &ArgSettings) { + match *s { + ArgSettings::Required => self.0.insert(REQUIRED), + ArgSettings::Multiple => self.0.insert(MULTIPLE), + ArgSettings::EmptyValues => self.0.insert(EMPTY_VALS), + ArgSettings::Global => self.0.insert(GLOBAL), + ArgSettings::Hidden => self.0.insert(HIDDEN), + } + } + + pub fn unset(&mut self, s: &ArgSettings) { + match *s { + ArgSettings::Required => self.0.remove(REQUIRED), + ArgSettings::Multiple => self.0.remove(MULTIPLE), + ArgSettings::EmptyValues => self.0.remove(EMPTY_VALS), + ArgSettings::Global => self.0.remove(GLOBAL), + ArgSettings::Hidden => self.0.remove(HIDDEN), + } + } + + pub fn is_set(&self, s: &ArgSettings) -> bool { + match *s { + ArgSettings::Required => self.0.contains(REQUIRED), + ArgSettings::Multiple => self.0.contains(MULTIPLE), + ArgSettings::EmptyValues => self.0.contains(EMPTY_VALS), + ArgSettings::Global => self.0.contains(GLOBAL), + ArgSettings::Hidden => self.0.contains(HIDDEN), + } + } +} + +#[doc(hidden)] +#[derive(Debug, PartialEq)] +pub enum ArgSettings { + Required, + Multiple, + EmptyValues, + Global, + Hidden, +} + +impl FromStr for ArgSettings { + type Err = String; + fn from_str(s: &str) -> Result::Err> { + match &*s.to_ascii_lowercase() { + "required" => Ok(ArgSettings::Required), + "multiple" => Ok(ArgSettings::Multiple), + "global" => Ok(ArgSettings::Global), + "emptyvalues" => Ok(ArgSettings::EmptyValues), + "hidden" => Ok(ArgSettings::Hidden), + _ => Err("unknown ArgSetting, cannot convert from str".to_owned()) + } + } +} \ No newline at end of file diff --git a/src/macros.rs b/src/macros.rs index 50063b45..907e96e1 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -56,6 +56,7 @@ macro_rules! vec_remove { } } if let Some(i) = ix { + $vec.dedup(); $vec.remove(i); } }