From a62e452754b3b0e3ac9a15aa8b5330636229ead1 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Thu, 4 Feb 2016 11:54:46 -0500 Subject: [PATCH 1/3] fix(AppSettings): fixes bug where subcmds didn't receive parent ver Subcommands now receive the parent version when the setting AppSettings::GlobalVersion has been set. --- src/app/mod.rs | 60 +++++++++++++++++++++++--------------------- src/app/parser.rs | 63 ++++++++++++++++++++++++++--------------------- 2 files changed, 67 insertions(+), 56 deletions(-) diff --git a/src/app/mod.rs b/src/app/mod.rs index 9026434661d..46497ef0bad 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -50,7 +50,11 @@ use errors::Result as ClapResult; /// // Your program logic starts here... /// ``` #[allow(missing_debug_implementations)] -pub struct App<'a, 'b>(Parser<'a, 'b>) where 'a: 'b; +pub struct App<'a, 'b> where 'a: 'b { + #[doc(hidden)] + pub p: Parser<'a, 'b> +} + impl<'a, 'b> App<'a, 'b> { /// Creates a new instance of an application requiring a name. The name may be, but doesn't @@ -64,7 +68,7 @@ impl<'a, 'b> App<'a, 'b> { /// let prog = App::new("My Program") /// # ; /// ``` - pub fn new>(n: S) -> Self { App(Parser::with_name(n.into())) } + pub fn new>(n: S) -> Self { App { p: Parser::with_name(n.into()) } } /// Creates a new instace of `App` from a .yml (YAML) file. A full example of supported YAML /// objects can be found in `examples/17_yaml.rs` and `examples/17_yaml.yml`. One great use for @@ -114,7 +118,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn author>(mut self, author: S) -> Self { - self.0.meta.author = Some(author.into()); + self.p.meta.author = Some(author.into()); self } @@ -136,7 +140,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn bin_name>(mut self, name: S) -> Self { - self.0.meta.bin_name = Some(name.into()); + self.p.meta.bin_name = Some(name.into()); self } @@ -152,7 +156,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn about>(mut self, about: S) -> Self { - self.0.meta.about = Some(about.into()); + self.p.meta.about = Some(about.into()); self } @@ -169,7 +173,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn after_help>(mut self, help: S) -> Self { - self.0.meta.more_help = Some(help.into()); + self.p.meta.more_help = Some(help.into()); self } @@ -189,7 +193,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn version>(mut self, ver: S) -> Self { - self.0.meta.version = Some(ver.into()); + self.p.meta.version = Some(ver.into()); self } @@ -217,7 +221,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn usage>(mut self, usage: S) -> Self { - self.0.meta.usage_str = Some(usage.into()); + self.p.meta.usage_str = Some(usage.into()); self } @@ -255,7 +259,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn help>(mut self, help: S) -> Self { - self.0.meta.help_str = Some(help.into()); + self.p.meta.help_str = Some(help.into()); self } @@ -277,7 +281,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn help_short + 'b>(mut self, s: S) -> Self { - self.0.help_short(s.as_ref()); + self.p.help_short(s.as_ref()); self } @@ -299,7 +303,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn version_short>(mut self, s: S) -> Self { - self.0.version_short(s.as_ref()); + self.p.version_short(s.as_ref()); self } @@ -317,7 +321,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn setting(mut self, setting: AppSettings) -> Self { - self.0.set(setting); + self.p.set(setting); self } @@ -336,7 +340,7 @@ impl<'a, 'b> App<'a, 'b> { /// ``` pub fn settings(mut self, settings: &[AppSettings]) -> Self { for s in settings { - self.0.set(*s); + self.p.set(*s); } self } @@ -362,7 +366,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn arg> + 'a>(mut self, a: A) -> Self { - self.0.add_arg(a.borrow()); + self.p.add_arg(a.borrow()); self } @@ -381,7 +385,7 @@ impl<'a, 'b> App<'a, 'b> { /// ``` pub fn args(mut self, args: &[Arg<'a, 'b>]) -> Self { for arg in args { - self.0.add_arg(arg); + self.p.add_arg(arg); } self } @@ -401,7 +405,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn arg_from_usage(mut self, usage: &'a str) -> Self { - self.0.add_arg(&Arg::from_usage(usage)); + self.p.add_arg(&Arg::from_usage(usage)); self } @@ -426,7 +430,7 @@ impl<'a, 'b> App<'a, 'b> { pub fn args_from_usage(mut self, usage: &'a str) -> Self { for l in usage.lines() { if l.len() == 0 { continue; } - self.0.add_arg(&Arg::from_usage(l.trim())); + self.p.add_arg(&Arg::from_usage(l.trim())); } self } @@ -464,7 +468,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn group(mut self, group: ArgGroup<'a>) -> Self { - self.0.add_group(group); + self.p.add_group(group); self } @@ -514,7 +518,7 @@ impl<'a, 'b> App<'a, 'b> { /// # ; /// ``` pub fn subcommand(mut self, subcmd: App<'a, 'b>) -> Self { - self.0.add_subcommand(subcmd); + self.p.add_subcommand(subcmd); self } @@ -536,7 +540,7 @@ impl<'a, 'b> App<'a, 'b> { where I: IntoIterator> { for subcmd in subcmds.into_iter() { - self.0.add_subcommand(subcmd); + self.p.add_subcommand(subcmd); } self } @@ -569,7 +573,7 @@ impl<'a, 'b> App<'a, 'b> { /// app.write_help(&mut out).ok().expect("failed to write to stdout"); /// ``` pub fn write_help(&self, w: &mut W) -> ClapResult<()> { - self.0.write_help(w) + self.p.write_help(w) } /// Starts the parsing process, upon a failed parse an error will be displayed to the user and @@ -690,10 +694,10 @@ impl<'a, 'b> App<'a, 'b> { T: Into { // Verify all positional assertions pass - self.0.verify_positionals(); + self.p.verify_positionals(); // If there are global arguments, we need to propgate them down to subcommands // before parsing incase we run into a subcommand - self.0.propogate_globals(); + self.p.propogate_globals(); let mut matcher = ArgMatcher::new(); @@ -705,14 +709,14 @@ impl<'a, 'b> App<'a, 'b> { // will have two arguments, './target/release/my_prog', '-a' but we don't want // to display // the full path when displaying help messages and such - if !self.0.is_set(AppSettings::NoBinaryName) { + if !self.p.is_set(AppSettings::NoBinaryName) { if let Some(name) = it.next() { let bn_os = name.into(); let p = Path::new(&*bn_os); if let Some(f) = p.file_name() { if let Some(s) = f.to_os_string().to_str() { - if let None = self.0.meta.bin_name { - self.0.meta.bin_name = Some(s.to_owned()); + if let None = self.p.meta.bin_name { + self.p.meta.bin_name = Some(s.to_owned()); } } } @@ -720,7 +724,7 @@ impl<'a, 'b> App<'a, 'b> { } // do the real parsing - if let Err(e) = self.0.get_matches_with(&mut matcher, &mut it) { + if let Err(e) = self.p.get_matches_with(&mut matcher, &mut it) { return Err(e); } @@ -732,7 +736,7 @@ impl<'a, 'b> App<'a, 'b> { fn maybe_wait_for_exit(&self, e: Error) -> ! { if e.use_stderr() { wlnerr!("{}", e.message); - if self.0.is_set(AppSettings::WaitOnError) { + if self.p.is_set(AppSettings::WaitOnError) { wlnerr!("\nPress [ENTER] / [RETURN] to continue..."); let mut s = String::new(); let i = io::stdin(); diff --git a/src/app/parser.rs b/src/app/parser.rs index ec408d86dd9..a1f3e39621f 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -37,14 +37,14 @@ pub struct Parser<'a, 'b> where 'a: 'b { // A list of positional arguments positionals: VecMap>, // A list of subcommands - subcommands: Vec>, + #[doc(hidden)] + pub subcommands: Vec>, groups: HashMap<&'a str, ArgGroup<'a>>, global_args: Vec>, overrides: Vec<&'b str>, help_short: Option, version_short: Option, settings: AppFlags, - version: Option<&'b str>, pub meta: AppMeta<'b>, } @@ -65,7 +65,6 @@ impl<'a, 'b> Default for Parser<'a, 'b> { global_args: vec![], overrides: vec![], settings: AppFlags::new(), - version: None, meta: AppMeta::new(), } } @@ -192,16 +191,24 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { } pub fn add_subcommand(&mut self, mut subcmd: App<'a, 'b>) { - if subcmd.0.meta.name == "help" { + debugln!("fn=Parser::add_subcommand;"); + debug!("Is help..."); + if subcmd.p.meta.name == "help" { + sdebugln!("Yes"); self.settings.set(AppSettings::NeedsSubcommandHelp); - } + } else { sdebugln!("No"); } + debug!("Using Setting VersionlessSubcommands..."); if self.settings.is_set(AppSettings::VersionlessSubcommands) { - subcmd.0.settings.set(AppSettings::DisableVersion); - } - if self.settings.is_set(AppSettings::GlobalVersion) && subcmd.0.meta.version.is_none() && - self.version.is_some() { - subcmd.0.meta.version = Some(self.version.unwrap()); - } + sdebugln!("Yes"); + subcmd.p.settings.set(AppSettings::DisableVersion); + } else { sdebugln!("No"); } + debug!("Using Setting GlobalVersion..."); + if self.settings.is_set(AppSettings::GlobalVersion) && subcmd.p.meta.version.is_none() && + self.meta.version.is_some() { + sdebugln!("Yes"); + subcmd = subcmd.setting(AppSettings::GlobalVersion) + .version(self.meta.version.unwrap()); + } else { sdebugln!("No"); } self.subcommands.push(subcmd); } @@ -386,10 +393,10 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { // done and to recursively call this method { for a in &self.global_args { - sc.0.add_arg(a); + sc.p.add_arg(a); } } - sc.0.propogate_globals(); + sc.p.propogate_globals(); } } @@ -427,7 +434,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { // Has the user already passed '--'? if !pos_only { - let pos_sc = self.subcommands.iter().any(|s| &s.0.meta.name[..] == &*arg_os); + let pos_sc = self.subcommands.iter().any(|s| &s.p.meta.name[..] == &*arg_os); if (!starts_new_arg || self.is_set(AppSettings::AllowLeadingHyphen)) && !pos_sc { // Check to see if parsing a value from an option if let Some(nvo) = needs_val_of { @@ -465,7 +472,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { break; } else if let Some(candidate) = suggestions::did_you_mean( &*arg_os.to_string_lossy(), - self.subcommands.iter().map(|s| &s.0.meta.name)) { + self.subcommands.iter().map(|s| &s.p.meta.name)) { return Err( Error::invalid_subcommand(arg_os.to_string_lossy().into_owned(), candidate, @@ -584,30 +591,30 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { mid_string.push_str(" "); if let Some(ref mut sc) = self.subcommands .iter_mut() - .filter(|s| &s.0.meta.name == &sc_name) + .filter(|s| &s.p.meta.name == &sc_name) .next() { let mut sc_matcher = ArgMatcher::new(); // bin_name should be parent's bin_name + [] + the sc's name separated by // a space - sc.0.meta.usage = Some(format!("{}{}{}", + sc.p.meta.usage = Some(format!("{}{}{}", self.meta.bin_name.as_ref().unwrap_or(&String::new()), if self.meta.bin_name.is_some() { &*mid_string } else { "" }, - &*sc.0.meta.name)); - sc.0.meta.bin_name = Some(format!("{}{}{}", + &*sc.p.meta.name)); + sc.p.meta.bin_name = Some(format!("{}{}{}", self.meta.bin_name.as_ref().unwrap_or(&String::new()), if self.meta.bin_name.is_some() { " " } else { "" }, - &*sc.0.meta.name)); - try!(sc.0.get_matches_with(&mut sc_matcher, it)); + &*sc.p.meta.name)); + try!(sc.p.get_matches_with(&mut sc_matcher, it)); matcher.subcommand(SubCommand { - name: sc.0.meta.name.clone(), + name: sc.p.meta.name.clone(), matches: sc_matcher.into(), }); } @@ -783,7 +790,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { if !self.subcommands.is_empty() && !self.subcommands .iter() - .any(|s| &s.0.meta.name[..] == "help") { + .any(|s| &s.p.meta.name[..] == "help") { self.subcommands.push(App::new("help").about("Prints this message")); } } @@ -1445,8 +1452,8 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { let mut longest_sc = 0; for scl in self.subcommands .iter() - .filter(|s| !s.0.is_set(AppSettings::Hidden)) - .map(|s| s.0.meta.name.len()) { + .filter(|s| !s.p.is_set(AppSettings::Hidden)) + .map(|s| s.p.meta.name.len()) { if scl > longest_sc { longest_sc = scl; } @@ -1509,12 +1516,12 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { if subcmds { try!(write!(w, "\nSUBCOMMANDS:\n")); for (name, sc) in self.subcommands.iter() - .filter(|s| !s.0.is_set(AppSettings::Hidden)) - .map(|s| (&s.0.meta.name[..], s)) + .filter(|s| !s.p.is_set(AppSettings::Hidden)) + .map(|s| (&s.p.meta.name[..], s)) .collect::>() { try!(write!(w, "{}{}", tab, name)); write_spaces!((longest_sc + 4) - (name.len()), w); - if let Some(a) = sc.0.meta.about { + if let Some(a) = sc.p.meta.about { if a.contains("{n}") { let mut ab = a.split("{n}"); while let Some(part) = ab.next() { From 4f207a1195fb4af38711fa66bda76ac16166438f Mon Sep 17 00:00:00 2001 From: Kevin K Date: Thu, 4 Feb 2016 11:55:58 -0500 Subject: [PATCH 2/3] tests(AppSettings): adds test for GlobalVersion --- tests/app_settings.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/app_settings.rs b/tests/app_settings.rs index da9228be9d3..6ffbb7559cf 100644 --- a/tests/app_settings.rs +++ b/tests/app_settings.rs @@ -13,6 +13,15 @@ fn sub_command_negate_required() { .get_matches_from(vec!["myprog", "sub1"]); } +#[test] +fn global_version() { + let app = App::new("global_version") + .setting(AppSettings::GlobalVersion) + .version("1.1") + .subcommand(SubCommand::with_name("sub1")); + assert_eq!(app.p.subcommands[0].p.meta.version, Some("1.1")); +} + #[test] fn sub_command_negate_required_2() { let result = App::new("sub_command_negate") @@ -145,8 +154,7 @@ fn app_settings_fromstr() { assert_eq!("strictutf8".parse::().unwrap(), AppSettings::StrictUtf8); assert_eq!("allowinvalidutf8".parse::().unwrap(), AppSettings::AllowInvalidUtf8); assert_eq!("allowleadinghyphen".parse::().unwrap(), AppSettings::AllowLeadingHyphen); - assert_eq!("hidepossiblevaluesinhelp".parse::().unwrap(), AppSettings::HidePossibleValuesInHelp); + assert_eq!("hidepossiblevaluesinhelp".parse::().unwrap(), AppSettings::HidePossibleValuesInHelp); assert_eq!("hidden".parse::().unwrap(), AppSettings::Hidden); assert!("hahahaha".parse::().is_err()); } - From ff259626f7d28ae0c2ff23829beacac7b6110d42 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Thu, 4 Feb 2016 11:56:47 -0500 Subject: [PATCH 3/3] chore: updates changelog --- CHANGELOG.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9fd51763f2..8ab45d94e81 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,10 +4,9 @@ #### Bug Fixes -* adds support for building ArgGroups from standalone YAML ([bbed7388](https://github.com/kbknapp/clap-rs/commit/bbed73889d8f241d493c9ef18ac5926856df1c24)) +* adds support for building ArgGroups from standalone YAML ([fcbc7e12](https://github.com/kbknapp/clap-rs/commit/fcbc7e12f5d7b023b8f30cba8cad28a01cf6cd26)) * Stop lonely hyphens from causing panic ([85b11468](https://github.com/kbknapp/clap-rs/commit/85b11468b0189d5cc15f1cfac5db40d17a0077dc), closes [#410](https://github.com/kbknapp/clap-rs/issues/410)) -* fixes cargo features to NOT require nightly with unstable features ([8c72e779](https://github.com/kbknapp/clap-rs/commit/8c72e77975e31fb8268cc2f34565f2e693eba935)) - +* **AppSettings:** fixes bug where subcmds didn't receive parent ver ([a62e4527](https://github.com/kbknapp/clap-rs/commit/a62e452754b3b0e3ac9a15aa8b5330636229ead1)) ### v2.0.3 (2016-02-02)