From aff89d579b5b85c3dc81b64f16d5865299ec39a2 Mon Sep 17 00:00:00 2001 From: Kevin K Date: Wed, 28 Oct 2015 06:45:45 -0400 Subject: [PATCH] feat: allows parsing without a binary name for daemons and interactive CLIs Closes #318 --- src/app/app.rs | 14 +++++++----- src/app/settings.rs | 51 +++++++++++++++++++++++++++++++------------ tests/app_settings.rs | 13 +++++++++++ 3 files changed, 58 insertions(+), 20 deletions(-) diff --git a/src/app/app.rs b/src/app/app.rs index ef11977a545..1d16e375481 100644 --- a/src/app/app.rs +++ b/src/app/app.rs @@ -1980,12 +1980,14 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{ // ./target/release/my_prog -a // 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 let Some(name) = it.next() { - let p = Path::new(name.as_ref()); - if let Some(f) = p.file_name() { - if let Ok(s) = f.to_os_string().into_string() { - if let None = self.bin_name { - self.bin_name = Some(s); + if !self.settings.is_set(&AppSettings::NoBinaryName) { + if let Some(name) = it.next() { + let p = Path::new(name.as_ref()); + if let Some(f) = p.file_name() { + if let Ok(s) = f.to_os_string().into_string() { + if let None = self.bin_name { + self.bin_name = Some(s); + } } } } diff --git a/src/app/settings.rs b/src/app/settings.rs index 6ed5d6c9ead..82418718dcf 100644 --- a/src/app/settings.rs +++ b/src/app/settings.rs @@ -3,20 +3,21 @@ use std::ascii::AsciiExt; bitflags! { flags Flags: u32 { - const SC_NEGATE_REQS = 0b00000000000001, - const SC_REQUIRED = 0b00000000000010, - const A_REQUIRED_ELSE_HELP = 0b00000000000100, - const GLOBAL_VERSION = 0b00000000001000, - const VERSIONLESS_SC = 0b00000000010000, - const UNIFIED_HELP = 0b00000000100000, - const WAIT_ON_ERROR = 0b00000001000000, - const SC_REQUIRED_ELSE_HELP= 0b00000010000000, - const NEEDS_LONG_HELP = 0b00000100000000, - const NEEDS_LONG_VERSION = 0b00001000000000, - const NEEDS_SC_HELP = 0b00010000000000, - const DISABLE_VERSION = 0b00100000000000, - const HIDDEN = 0b01000000000000, - const TRAILING_VARARG = 0b10000000000000, + const SC_NEGATE_REQS = 0b000000000000001, + const SC_REQUIRED = 0b000000000000010, + const A_REQUIRED_ELSE_HELP = 0b000000000000100, + const GLOBAL_VERSION = 0b000000000001000, + const VERSIONLESS_SC = 0b000000000010000, + const UNIFIED_HELP = 0b000000000100000, + const WAIT_ON_ERROR = 0b000000001000000, + const SC_REQUIRED_ELSE_HELP= 0b000000010000000, + const NEEDS_LONG_HELP = 0b000000100000000, + const NEEDS_LONG_VERSION = 0b000001000000000, + const NEEDS_SC_HELP = 0b000010000000000, + const DISABLE_VERSION = 0b000100000000000, + const HIDDEN = 0b001000000000000, + const TRAILING_VARARG = 0b010000000000000, + const NO_BIN_NAME = 0b100000000000000, } } @@ -43,6 +44,7 @@ impl AppFlags { AppSettings::DisableVersion => self.0.insert(DISABLE_VERSION), AppSettings::Hidden => self.0.insert(HIDDEN), AppSettings::TrailingVarArg => self.0.insert(TRAILING_VARARG), + AppSettings::NoBinaryName => self.0.insert(NO_BIN_NAME), } } @@ -62,6 +64,7 @@ impl AppFlags { AppSettings::DisableVersion => self.0.remove(DISABLE_VERSION), AppSettings::Hidden => self.0.remove(HIDDEN), AppSettings::TrailingVarArg => self.0.remove(TRAILING_VARARG), + AppSettings::NoBinaryName => self.0.remove(NO_BIN_NAME), } } @@ -81,6 +84,7 @@ impl AppFlags { AppSettings::DisableVersion => self.0.contains(DISABLE_VERSION), AppSettings::Hidden => self.0.contains(HIDDEN), AppSettings::TrailingVarArg => self.0.contains(TRAILING_VARARG), + AppSettings::NoBinaryName => self.0.contains(NO_BIN_NAME), } } } @@ -256,6 +260,25 @@ pub enum AppSettings { /// assert_eq!(m.values_of("cmd").unwrap(), &["some_command", "-r", "set"]); /// ``` TrailingVarArg, + /// Specifies that the parser should not assume the first argument passed is the binary name. + /// This is normally the case when using a "daemon" style mode, or an interactive CLI where one + /// one would not normally type the binary or program name for each command. + /// + /// **NOTE:** This should only be used when you absolutely know it's what you need. 99% of the + /// cases out there don't need this setting. + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{App, Arg, AppSettings}; + /// let m = App::new("myprog") + /// .setting(AppSettings::NoBinaryName) + /// .arg(Arg::from_usage("... 'commands to run'")) + /// .get_matches_from(vec!["some_command", "-r", "set"]); + /// + /// assert_eq!(m.values_of("cmd").unwrap(), &["some_command", "-r", "set"]); + /// ``` + NoBinaryName, #[doc(hidden)] NeedsLongVersion, #[doc(hidden)] diff --git a/tests/app_settings.rs b/tests/app_settings.rs index 6c846193021..f16ff6a1acf 100644 --- a/tests/app_settings.rs +++ b/tests/app_settings.rs @@ -51,6 +51,19 @@ fn arg_required_else_help() { assert_eq!(err.error_type, ClapErrorType::MissingRequiredArgument); } +#[test] +fn no_bin_name() { + let result = App::new("arg_required") + .setting(AppSettings::NoBinaryName) + .arg(Arg::with_name("test") + .required(true) + .index(1)) + .get_matches_from_safe(vec!["testing"]); + assert!(result.is_ok()); + let matches = result.unwrap(); + assert_eq!(matches.value_of("test").unwrap(), "testing"); +} + #[test] fn unified_help() { let mut app = App::new("test")