diff --git a/CHANGELOG.md b/CHANGELOG.md index c8c6beab..19ee5e55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,34 @@ + +## v2.23.0 (2017-04-05) + + +#### API Additions + +* `App::long_about` +* `App::long_version` +* `App::print_long_help` +* `App::write_long_help` +* `App::print_long_version` +* `App::write_long_version` +* `Arg::long_help` + +#### Features + +* allows distinguishing between short and long version messages (-V/short or --version/long) ([59272b06](https://github.com/kbknapp/clap-rs/commit/59272b06cc213289dc604dbc694cb95d383a5d68)) +* allows distinguishing between short and long help with subcommands in the same manner as args ([6b371891](https://github.com/kbknapp/clap-rs/commit/6b371891a1702173a849d1e95f9fecb168bf6fc4)) +* allows specifying a short help vs a long help (i.e. varying levels of detail depending on if -h or --help was used) ([ef1b24c3](https://github.com/kbknapp/clap-rs/commit/ef1b24c3a0dff2f58c5e2e90880fbc2b69df20ee)) +* **clap_app!:** adds support for arg names with hyphens similar to longs with hyphens ([f7a88779](https://github.com/kbknapp/clap-rs/commit/f7a8877978c8f90e6543d4f0d9600c086cf92cd7), closes [#869](https://github.com/kbknapp/clap-rs/issues/869)) + +#### Bug Fixes + +* fixes a bug that wasn't allowing help and version to be properly overridden ([8b2ceb83](https://github.com/kbknapp/clap-rs/commit/8b2ceb8368bcb70689fadf1c7f4b9549184926c1), closes [#922](https://github.com/kbknapp/clap-rs/issues/922)) + +#### Documentation + +* **clap_app!:** documents the `--("some-arg")` method for using args with hyphens inside them ([bc08ef3e](https://github.com/kbknapp/clap-rs/commit/bc08ef3e185393073d969d301989b6319c616c1f), closes [#919](https://github.com/kbknapp/clap-rs/issues/919)) + + + ### v2.22.2 (2017-03-30) diff --git a/Cargo.toml b/Cargo.toml index b41c32ac..306fd5c9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "clap" -version = "2.22.2" +version = "2.23.0" authors = ["Kevin K. "] exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"] repository = "https://github.com/kbknapp/clap-rs.git" diff --git a/README.md b/README.md index 876e9eb7..36b8677e 100644 --- a/README.md +++ b/README.md @@ -45,12 +45,24 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc) ## What's New -Here's the highlights for v2.22.2 +Here's the highlights for v2.23.0 + +* allows specifying a short help vs a long help (i.e. varying levels of detail depending on if -h or --help was used) +* **clap_app!:** adds support for arg names with hyphens similar to longs with hyphens +* fixes a bug that wasn't allowing help and version to be properly overridden + * This may break code that was relying on this bug! If you add a flag with a long of `help` manually *and* rely on the help message to be printed automatically your code could break. Please see the commit link in the full CHANGELOG.md +* `App::long_about` +* `App::long_version` +* `App::print_long_help` +* `App::write_long_help` +* `App::print_long_version` +* `App::write_long_version` +* `Arg::long_help` +* **clap_app!:** documents the `--("some-arg")` method for using args with hyphens inside them + +Here's the highlights for v2.21.0 to v2.22.2 * fixes the usage string regression when using help templates - -Here's the highlights for v2.21.0 to v2.22.1 - * fixes a big regression with custom usage strings * adds the ability to change the name of the App instance after creation * adds ability to hide the default value of an argument from the help string diff --git a/src/app/help.rs b/src/app/help.rs index 1b400ef4..c79142fe 100644 --- a/src/app/help.rs +++ b/src/app/help.rs @@ -89,6 +89,7 @@ pub struct Help<'a> { cizer: Colorizer, longest: usize, force_next_line: bool, + use_long: bool, } // Public Functions @@ -100,7 +101,8 @@ impl<'a> Help<'a> { color: bool, cizer: Colorizer, term_w: Option, - max_w: Option) + max_w: Option, + use_long: bool) -> Self { debugln!("Help::new;"); Help { @@ -121,21 +123,22 @@ impl<'a> Help<'a> { cizer: cizer, longest: 0, force_next_line: false, + use_long: use_long, } } /// Reads help settings from an App /// and write its help to the wrapped stream. - pub fn write_app_help(w: &'a mut Write, app: &App) -> ClapResult<()> { + pub fn write_app_help(w: &'a mut Write, app: &App, use_long: bool) -> ClapResult<()> { debugln!("Help::write_app_help;"); - Self::write_parser_help(w, &app.p) + Self::write_parser_help(w, &app.p, use_long) } /// Reads help settings from a Parser /// and write its help to the wrapped stream. - pub fn write_parser_help(w: &'a mut Write, parser: &Parser) -> ClapResult<()> { + pub fn write_parser_help(w: &'a mut Write, parser: &Parser, use_long: bool) -> ClapResult<()> { debugln!("Help::write_parser_help;"); - Self::_write_parser_help(w, parser, false) + Self::_write_parser_help(w, parser, false, use_long) } /// Reads help settings from a Parser @@ -143,11 +146,11 @@ impl<'a> Help<'a> { /// formatting when required. pub fn write_parser_help_to_stderr(w: &'a mut Write, parser: &Parser) -> ClapResult<()> { debugln!("Help::write_parser_help;"); - Self::_write_parser_help(w, parser, true) + Self::_write_parser_help(w, parser, true, false) } #[doc(hidden)] - pub fn _write_parser_help(w: &'a mut Write, parser: &Parser, stderr: bool) -> ClapResult<()> { + pub fn _write_parser_help(w: &'a mut Write, parser: &Parser, stderr: bool, use_long: bool) -> ClapResult<()> { debugln!("Help::write_parser_help;"); let nlh = parser.is_set(AppSettings::NextLineHelp); let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp); @@ -162,8 +165,9 @@ impl<'a> Help<'a> { color, cizer, parser.meta.term_w, - parser.meta.max_w) - .write_help(parser) + parser.meta.max_w, + use_long) + .write_help(parser) } /// Writes the parser help to the wrapped stream. @@ -191,8 +195,9 @@ impl<'a> Help<'a> { self.longest = 2; let mut arg_v = Vec::with_capacity(10); for arg in args.filter(|arg| { - !(arg.is_set(ArgSettings::Hidden)) || arg.is_set(ArgSettings::NextLineHelp) - }) { + !(arg.is_set(ArgSettings::Hidden)) || + arg.is_set(ArgSettings::NextLineHelp) + }) { if arg.longest_filter() { self.longest = cmp::max(self.longest, arg.to_string().len()); } @@ -432,8 +437,12 @@ impl<'a> Help<'a> { fn help<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, spec_vals: &str) -> io::Result<()> { debugln!("Help::help;"); let mut help = String::new(); - let h = arg.help().unwrap_or(""); - let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp); + let h = if self.use_long { + arg.long_help().unwrap_or(arg.help().unwrap_or("")) + } else { + arg.help().unwrap_or(arg.long_help().unwrap_or("")) + }; + let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp) || self.use_long; debugln!("Help::help: Next Line...{:?}", nlh); let spcs = if nlh || self.force_next_line { @@ -513,7 +522,8 @@ impl<'a> Help<'a> { debugln!("Help::spec_vals: Found aliases...{:?}", aliases); spec_vals.push(format!(" [aliases: {}]", if self.color { - aliases.iter() + aliases + .iter() .map(|v| format!("{}", self.cizer.good(v))) .collect::>() .join(", ") @@ -525,14 +535,14 @@ impl<'a> Help<'a> { if let Some(pv) = a.possible_vals() { debugln!("Help::spec_vals: Found possible vals...{:?}", pv); spec_vals.push(if self.color { - format!(" [values: {}]", - pv.iter() - .map(|v| format!("{}", self.cizer.good(v))) - .collect::>() - .join(", ")) - } else { - format!(" [values: {}]", pv.join(", ")) - }); + format!(" [values: {}]", + pv.iter() + .map(|v| format!("{}", self.cizer.good(v))) + .collect::>() + .join(", ")) + } else { + format!(" [values: {}]", pv.join(", ")) + }); } } spec_vals.join(" ") @@ -548,7 +558,10 @@ impl<'a> Help<'a> { pub fn write_all_args(&mut self, parser: &Parser) -> ClapResult<()> { debugln!("Help::write_all_args;"); let flags = parser.has_flags(); - let pos = parser.positionals().filter(|arg| !arg.is_set(ArgSettings::Hidden)).count() > 0; + let pos = parser + .positionals() + .filter(|arg| !arg.is_set(ArgSettings::Hidden)) + .count() > 0; let opts = parser.has_opts(); let subcmds = parser.has_subcommands(); @@ -557,7 +570,8 @@ impl<'a> Help<'a> { let mut first = true; if unified_help && (flags || opts) { - let opts_flags = parser.flags() + let opts_flags = parser + .flags() .map(as_arg_trait) .chain(parser.opts().map(as_arg_trait)); try!(color!(self, "OPTIONS:\n", warning)); @@ -566,8 +580,7 @@ impl<'a> Help<'a> { } else { if flags { try!(color!(self, "FLAGS:\n", warning)); - try!(self.write_args(parser.flags() - .map(as_arg_trait))); + try!(self.write_args(parser.flags().map(as_arg_trait))); first = false; } if opts { @@ -606,8 +619,13 @@ impl<'a> Help<'a> { // The shortest an arg can legally be is 2 (i.e. '-x') self.longest = 2; let mut ord_m = VecMap::new(); - for sc in parser.subcommands.iter().filter(|s| !s.p.is_set(AppSettings::Hidden)) { - let btm = ord_m.entry(sc.p.meta.disp_ord).or_insert(BTreeMap::new()); + for sc in parser + .subcommands + .iter() + .filter(|s| !s.p.is_set(AppSettings::Hidden)) { + let btm = ord_m + .entry(sc.p.meta.disp_ord) + .or_insert(BTreeMap::new()); self.longest = cmp::max(self.longest, sc.p.meta.name.len()); btm.insert(sc.p.meta.name.clone(), sc.clone()); } @@ -861,9 +879,9 @@ impl<'a> Help<'a> { debugln!("Help::write_template_help:iter: tag_buf={};", unsafe { String::from_utf8_unchecked(tag_buf.get_ref()[0..tag_length] - .iter() - .map(|&i| i) - .collect::>()) + .iter() + .map(|&i| i) + .collect::>()) }); match &tag_buf.get_ref()[0..tag_length] { b"?" => { @@ -894,22 +912,20 @@ impl<'a> Help<'a> { try!(self.write_all_args(&parser)); } b"unified" => { - let opts_flags = parser.flags() + let opts_flags = parser + .flags() .map(as_arg_trait) .chain(parser.opts().map(as_arg_trait)); try!(self.write_args(opts_flags)); } b"flags" => { - try!(self.write_args(parser.flags() - .map(as_arg_trait))); + try!(self.write_args(parser.flags().map(as_arg_trait))); } b"options" => { - try!(self.write_args(parser.opts() - .map(as_arg_trait))); + try!(self.write_args(parser.opts().map(as_arg_trait))); } b"positionals" => { - try!(self.write_args(parser.positionals() - .map(as_arg_trait))); + try!(self.write_args(parser.positionals().map(as_arg_trait))); } b"subcommands" => { try!(self.write_subcommands(&parser)); diff --git a/src/app/meta.rs b/src/app/meta.rs index 33b2f355..6fbf412c 100644 --- a/src/app/meta.rs +++ b/src/app/meta.rs @@ -1,11 +1,14 @@ #[doc(hidden)] #[allow(missing_debug_implementations)] +#[derive(Default, Clone)] pub struct AppMeta<'b> { pub name: String, pub bin_name: Option, pub author: Option<&'b str>, pub version: Option<&'b str>, + pub long_version: Option<&'b str>, pub about: Option<&'b str>, + pub long_about: Option<&'b str>, pub more_help: Option<&'b str>, pub pre_help: Option<&'b str>, pub aliases: Option>, // (name, visible) @@ -18,51 +21,7 @@ pub struct AppMeta<'b> { pub template: Option<&'b str>, } -impl<'b> Default for AppMeta<'b> { - fn default() -> Self { - AppMeta { - name: String::new(), - author: None, - about: None, - more_help: None, - pre_help: None, - version: None, - usage_str: None, - usage: None, - bin_name: None, - help_str: None, - disp_ord: 999, - template: None, - aliases: None, - term_w: None, - max_w: None, - } - } -} - impl<'b> AppMeta<'b> { pub fn new() -> Self { Default::default() } - pub fn with_name(s: String) -> Self { AppMeta { name: s, ..Default::default() } } -} - -impl<'b> Clone for AppMeta<'b> { - fn clone(&self) -> Self { - AppMeta { - name: self.name.clone(), - author: self.author, - about: self.about, - more_help: self.more_help, - pre_help: self.pre_help, - version: self.version, - usage_str: self.usage_str, - usage: self.usage.clone(), - bin_name: self.bin_name.clone(), - help_str: self.help_str, - disp_ord: self.disp_ord, - template: self.template, - aliases: self.aliases.clone(), - term_w: self.term_w, - max_w: self.max_w, - } - } -} + pub fn with_name(s: String) -> Self { AppMeta { name: s, disp_ord: 999, ..Default::default() } } +} \ No newline at end of file diff --git a/src/app/mod.rs b/src/app/mod.rs index 428bcdbd..0c9f9aee 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -200,7 +200,13 @@ impl<'a, 'b> App<'a, 'b> { } /// Sets a string describing what the program does. This will be displayed when displaying help - /// information. + /// information with `-h`. + /// + /// **NOTE:** If only `about` is provided, and not [`App::long_about`] but the user requests + /// `--help` clap will still display the contents of `about` appropriately + /// + /// **NOTE:** Only [`App::about`] is used in completion script generation in order to be + /// concise /// /// # Examples /// @@ -210,11 +216,38 @@ impl<'a, 'b> App<'a, 'b> { /// .about("Does really amazing things to great people") /// # ; /// ``` + /// [`App::long_about`]: ./struct.App.html#method.long_about pub fn about>(mut self, about: S) -> Self { self.p.meta.about = Some(about.into()); self } + /// Sets a string describing what the program does. This will be displayed when displaying help + /// information. + /// + /// **NOTE:** If only `long_about` is provided, and not [`App::about`] but the user requests + /// `-h` clap will still display the contents of `long_about` appropriately + /// + /// **NOTE:** Only [`App::about`] is used in completion script generation in order to be + /// concise + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// App::new("myprog") + /// .long_about( + /// "Does really amazing things to great people. Now let's talk a little + /// more in depth about how this subcommand really works. It may take about + /// a few lines of text, but that's ok!") + /// # ; + /// ``` + /// [`App::about`]: ./struct.App.html#method.about + pub fn long_about>(mut self, about: S) -> Self { + self.p.meta.long_about = Some(about.into()); + self + } + /// Sets the program's name. This will be displayed when displaying help information. /// /// **Pro-top:** This function is particularly useful when configuring a program via @@ -277,7 +310,10 @@ impl<'a, 'b> App<'a, 'b> { } /// Sets a string of the version number to be displayed when displaying version or help - /// information. + /// information with `-V`. + /// + /// **NOTE:** If only `version` is provided, and not [`App::long_version`] but the user + /// requests `--version` clap will still display the contents of `version` appropriately /// /// **Pro-tip:** Use `clap`s convenience macro [`crate_version!`] to automatically set your /// application's version to the same thing as your crate at compile time. See the [`examples/`] @@ -293,11 +329,43 @@ impl<'a, 'b> App<'a, 'b> { /// ``` /// [`crate_version!`]: ./macro.crate_version!.html /// [`examples/`]: https://github.com/kbknapp/clap-rs/tree/master/examples + /// [`App::long_version`]: ./struct.App.html#method.long_version pub fn version>(mut self, ver: S) -> Self { self.p.meta.version = Some(ver.into()); self } + /// Sets a string of the version number to be displayed when displaying version or help + /// information with `--version`. + /// + /// **NOTE:** If only `long_version` is provided, and not [`App::version`] but the user + /// requests `-V` clap will still display the contents of `long_version` appropriately + /// + /// **Pro-tip:** Use `clap`s convenience macro [`crate_version!`] to automatically set your + /// application's version to the same thing as your crate at compile time. See the [`examples/`] + /// directory for more information + /// + /// # Examples + /// + /// ```no_run + /// # use clap::{App, Arg}; + /// App::new("myprog") + /// .long_version( + /// "v0.1.24 + /// commit: abcdef89726d + /// revision: 123 + /// release: 2 + /// binary: myprog") + /// # ; + /// ``` + /// [`crate_version!`]: ./macro.crate_version!.html + /// [`examples/`]: https://github.com/kbknapp/clap-rs/tree/master/examples + /// [`App::version`]: ./struct.App.html#method.version + pub fn long_version>(mut self, ver: S) -> Self { + self.p.meta.long_version = Some(ver.into()); + self + } + /// Sets a custom usage string to override the auto-generated usage string. /// /// This will be displayed to the user when errors are found in argument parsing, or when you @@ -1062,7 +1130,11 @@ impl<'a, 'b> App<'a, 'b> { self } - /// Prints the full help message to [`io::stdout()`] using a [`BufWriter`] + /// Prints the full help message to [`io::stdout()`] using a [`BufWriter`] using the same + /// method as if someone ran `-h` to request the help message + /// + /// **NOTE:** clap has the ability to distinguish between "short" and "long" help messages + /// depending on if the user ran [`-h` (short)] or [`--help` (long)] /// /// # Examples /// @@ -1073,6 +1145,8 @@ impl<'a, 'b> App<'a, 'b> { /// ``` /// [`io::stdout()`]: https://doc.rust-lang.org/std/io/fn.stdout.html /// [`BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html + /// [`-h` (short)]: ./struct.Arg.html#method.help + /// [`--help` (long)]: ./struct.Arg.html#method.long_help pub fn print_help(&mut self) -> ClapResult<()> { // If there are global arguments, or settings we need to propgate them down to subcommands // before parsing incase we run into a subcommand @@ -1086,7 +1160,45 @@ impl<'a, 'b> App<'a, 'b> { self.write_help(&mut buf_w) } - /// Writes the full help message to the user to a [`io::Write`] object + /// Prints the full help message to [`io::stdout()`] using a [`BufWriter`] using the same + /// method as if someone ran `-h` to request the help message + /// + /// **NOTE:** clap has the ability to distinguish between "short" and "long" help messages + /// depending on if the user ran [`-h` (short)] or [`--help` (long)] + /// + /// # Examples + /// + /// ```rust + /// # use clap::App; + /// let mut app = App::new("myprog"); + /// app.print_long_help(); + /// ``` + /// [`io::stdout()`]: https://doc.rust-lang.org/std/io/fn.stdout.html + /// [`BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.html + /// [`-h` (short)]: ./struct.Arg.html#method.help + /// [`--help` (long)]: ./struct.Arg.html#method.long_help + pub fn print_long_help(&mut self) -> ClapResult<()> { + // If there are global arguments, or settings we need to propgate them down to subcommands + // before parsing incase we run into a subcommand + self.p.propogate_globals(); + self.p.propogate_settings(); + self.p.derive_display_order(); + + self.p.create_help_and_version(); + let out = io::stdout(); + let mut buf_w = BufWriter::new(out.lock()); + self.write_long_help(&mut buf_w) + } + + /// Writes the full help message to the user to a [`io::Write`] object in the same method as if + /// the user ran `-h` + /// + /// **NOTE:** clap has the ability to distinguish between "short" and "long" help messages + /// depending on if the user ran [`-h` (short)] or [`--help` (long)] + /// + /// **NOTE:** There is a known bug where this method does not write propogated global arguments + /// or autogenerated arguments (i.e. the default help/version args). Prefer + /// [`App::write_long_help`] instead if possibe! /// /// # Examples /// @@ -1098,6 +1210,8 @@ impl<'a, 'b> App<'a, 'b> { /// app.write_help(&mut out).expect("failed to write to stdout"); /// ``` /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html + /// [`-h` (short)]: ./struct.Arg.html#method.help + /// [`--help` (long)]: ./struct.Arg.html#method.long_help pub fn write_help(&self, w: &mut W) -> ClapResult<()> { // PENDING ISSUE: 808 // https://github.com/kbknapp/clap-rs/issues/808 @@ -1108,10 +1222,40 @@ impl<'a, 'b> App<'a, 'b> { // self.p.derive_display_order(); // self.p.create_help_and_version(); - Help::write_app_help(w, self) + Help::write_app_help(w, self, false) } - /// Writes the version message to the user to a [`io::Write`] object + /// Writes the full help message to the user to a [`io::Write`] object in the same method as if + /// the user ran `--help` + /// + /// **NOTE:** clap has the ability to distinguish between "short" and "long" help messages + /// depending on if the user ran [`-h` (short)] or [`--help` (long)] + /// + /// # Examples + /// + /// ```rust + /// # use clap::App; + /// use std::io; + /// let mut app = App::new("myprog"); + /// let mut out = io::stdout(); + /// app.write_long_help(&mut out).expect("failed to write to stdout"); + /// ``` + /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html + /// [`-h` (short)]: ./struct.Arg.html#method.help + /// [`--help` (long)]: ./struct.Arg.html#method.long_help + pub fn write_long_help(&mut self, w: &mut W) -> ClapResult<()> { + self.p.propogate_globals(); + self.p.propogate_settings(); + self.p.derive_display_order(); + self.p.create_help_and_version(); + + Help::write_app_help(w, self, true) + } + + /// Writes the version message to the user to a [`io::Write`] object as if the user ran `-V`. + /// + /// **NOTE:** clap has the ability to distinguish between "short" and "long" version messages + /// depending on if the user ran [`-V` (short)] or [`--version` (long)] /// /// # Examples /// @@ -1123,10 +1267,32 @@ impl<'a, 'b> App<'a, 'b> { /// app.write_version(&mut out).expect("failed to write to stdout"); /// ``` /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html + /// [`-V` (short)]: ./struct.App.html#method.version + /// [`--version` (long)]: ./struct.App.html#method.long_version pub fn write_version(&self, w: &mut W) -> ClapResult<()> { - self.p.write_version(w).map_err(From::from) + self.p.write_version(w, false).map_err(From::from) } + /// Writes the version message to the user to a [`io::Write`] object + /// + /// **NOTE:** clap has the ability to distinguish between "short" and "long" version messages + /// depending on if the user ran [`-V` (short)] or [`--version` (long)] + /// + /// # Examples + /// + /// ```rust + /// # use clap::App; + /// use std::io; + /// let mut app = App::new("myprog"); + /// let mut out = io::stdout(); + /// app.write_long_version(&mut out).expect("failed to write to stdout"); + /// ``` + /// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html + /// [`-V` (short)]: ./struct.App.html#method.version + /// [`--version` (long)]: ./struct.App.html#method.long_version + pub fn write_long_version(&self, w: &mut W) -> ClapResult<()> { + self.p.write_version(w, true).map_err(From::from) + } /// Generate a completions file for a specified shell at compile time. /// @@ -1183,7 +1349,7 @@ impl<'a, 'b> App<'a, 'b> { /// build = "build.rs" /// /// [build-dependencies] - /// clap = "2.9" + /// clap = "2.23" /// ``` /// /// Next, we place a `build.rs` in our project root. @@ -1622,6 +1788,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for App<'n, 'e> { fn val_delim(&self) -> Option { None } fn takes_value(&self) -> bool { true } fn help(&self) -> Option<&'e str> { self.p.meta.about } + fn long_help(&self) -> Option<&'e str> { self.p.meta.long_about } fn default_val(&self) -> Option<&'e OsStr> { None } fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { None diff --git a/src/app/parser.rs b/src/app/parser.rs index d0cdae9e..4c6b8664 100644 --- a/src/app/parser.rs +++ b/src/app/parser.rs @@ -63,7 +63,10 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b { pub fn with_name(n: String) -> Self { - Parser { meta: AppMeta::with_name(n), ..Default::default() } + Parser { + meta: AppMeta::with_name(n), + ..Default::default() + } } pub fn help_short(&mut self, s: &str) { @@ -98,11 +101,7 @@ impl<'a, 'b> Parser<'a, 'b> use std::error::Error; let out_dir = PathBuf::from(od); - let name = &*self.meta - .bin_name - .as_ref() - .unwrap() - .clone(); + let name = &*self.meta.bin_name.as_ref().unwrap().clone(); let file_name = match for_shell { Shell::Bash => format!("{}.bash-completion", name), Shell::Fish => format!("{}.fish", name), @@ -146,7 +145,9 @@ impl<'a, 'b> Parser<'a, 'b> global and required", a.b.name); if a.b.is_set(ArgSettings::Last) { - assert!(!self.positionals.values().any(|p| p.b.is_set(ArgSettings::Last)), + assert!(!self.positionals + .values() + .any(|p| p.b.is_set(ArgSettings::Last)), "Only one positional argument may have last(true) set. Found two."); assert!(a.s.long.is_none(), "Flags or Options may not have last(true) set. {} has both a long and last(true) set.", @@ -190,7 +191,10 @@ impl<'a, 'b> Parser<'a, 'b> if a.is_set(ArgSettings::Required) { // If the arg is required, add all it's requirements to master required list if let Some(ref areqs) = a.b.requires { - for name in areqs.iter().filter(|&&(val, _)| val.is_none()).map(|&(_, name)| name) { + for name in areqs + .iter() + .filter(|&&(val, _)| val.is_none()) + .map(|&(_, name)| name) { self.required.push(name); } } @@ -206,6 +210,13 @@ impl<'a, 'b> Parser<'a, 'b> self.set(AS::DontCollapseArgsInUsage); self.set(AS::ContainsLast); } + if let Some(l) = a.s.long { + if l == "version" { + self.unset(AS::NeedsLongVersion); + } else if l == "help" { + self.unset(AS::NeedsLongHelp); + } + } } // actually adds the arguments @@ -225,7 +236,8 @@ impl<'a, 'b> Parser<'a, 'b> } else { a.index.unwrap() as usize }; - self.positionals.insert(i, PosBuilder::from_arg(a, i as u64)); + self.positionals + .insert(i, PosBuilder::from_arg(a, i as u64)); } else if a.is_set(ArgSettings::TakesValue) { let mut ob = OptBuilder::from(a); ob.s.unified_ord = self.flags.len() + self.opts.len(); @@ -319,11 +331,7 @@ impl<'a, 'b> Parser<'a, 'b> if vsc { sc.p.set(AS::DisableVersion); } - if gv && - sc.p - .meta - .version - .is_none() && self.meta.version.is_some() { + if gv && sc.p.meta.version.is_none() && self.meta.version.is_some() { sc.p.set(AS::GlobalVersion); sc.p.meta.version = Some(self.meta.version.unwrap()); } @@ -397,7 +405,9 @@ impl<'a, 'b> Parser<'a, 'b> if self.flags.is_empty() { return false; } - self.flags.iter().any(|f| !f.is_set(ArgSettings::Hidden)) + self.flags + .iter() + .any(|f| !f.is_set(ArgSettings::Hidden)) } #[inline] @@ -405,7 +415,9 @@ impl<'a, 'b> Parser<'a, 'b> if self.positionals.is_empty() { return false; } - self.positionals.values().any(|p| !p.is_set(ArgSettings::Hidden)) + self.positionals + .values() + .any(|p| !p.is_set(ArgSettings::Hidden)) } #[inline] @@ -433,10 +445,7 @@ impl<'a, 'b> Parser<'a, 'b> // Firt we verify that the index highest supplied index, is equal to the number of // positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3 // but no 2) - if let Some((idx, p)) = self.positionals - .iter() - .rev() - .next() { + if let Some((idx, p)) = self.positionals.iter().rev().next() { assert!(!(idx != self.positionals.len()), "Found positional argument \"{}\" who's index is {} but there \ are only {} positional arguments defined", @@ -446,10 +455,12 @@ impl<'a, 'b> Parser<'a, 'b> } // Next we verify that only the highest index has a .multiple(true) (if any) - if self.positionals.values().any(|a| { - a.b.is_set(ArgSettings::Multiple) && - (a.index as usize != self.positionals.len()) - }) { + if self.positionals + .values() + .any(|a| { + a.b.is_set(ArgSettings::Multiple) && + (a.index as usize != self.positionals.len()) + }) { let mut it = self.positionals.values().rev(); let last = it.next().unwrap(); let second_to_last = it.next().unwrap(); @@ -535,10 +546,11 @@ impl<'a, 'b> Parser<'a, 'b> } } } - if self.positionals.values().any(|p| { - p.b.is_set(ArgSettings::Last) && - p.b.is_set(ArgSettings::Required) - }) && self.has_subcommands() && + if self.positionals + .values() + .any(|p| { + p.b.is_set(ArgSettings::Last) && p.b.is_set(ArgSettings::Required) + }) && self.has_subcommands() && !self.is_set(AS::SubcommandsNegateReqs) { panic!("Having a required positional argument with .last(true) set *and* child \ subcommands without setting SubcommandsNegateReqs isn't compatible."); @@ -587,10 +599,7 @@ impl<'a, 'b> Parser<'a, 'b> .iter() .filter(|s| { starts(&s.p.meta.name[..], &*arg_os) || - (s.p - .meta - .aliases - .is_some() && + (s.p.meta.aliases.is_some() && s.p .meta .aliases @@ -673,7 +682,7 @@ impl<'a, 'b> Parser<'a, 'b> if sc.meta.bin_name != self.meta.bin_name { sc.meta.bin_name = Some(format!("{} {}", bin_name, sc.meta.name)); } - sc._help() + sc._help(false) } // allow wrong self convention due to self.valid_neg_num = true and it's a private method @@ -743,11 +752,16 @@ impl<'a, 'b> Parser<'a, 'b> debugln!("Parser::get_matches_with;"); // Verify all positional assertions pass debug_assert!(self.verify_positionals()); - if self.positionals.values().any(|a| { - a.b.is_set(ArgSettings::Multiple) && - (a.index as usize != self.positionals.len()) - }) && - self.positionals.values().last().map_or(false, |p| !p.is_set(ArgSettings::Last)) { + if self.positionals + .values() + .any(|a| { + a.b.is_set(ArgSettings::Multiple) && + (a.index as usize != self.positionals.len()) + }) && + self.positionals + .values() + .last() + .map_or(false, |p| !p.is_set(ArgSettings::Last)) { self.settings.set(AS::LowIndexMultiplePositional); } let has_args = self.has_args(); @@ -836,7 +850,8 @@ impl<'a, 'b> Parser<'a, 'b> !self.is_set(AS::InferSubcommands) { if let Some(cdate) = suggestions::did_you_mean(&*arg_os.to_string_lossy(), sc_names!(self)) { - return Err(Error::invalid_subcommand(arg_os.to_string_lossy() + return Err(Error::invalid_subcommand(arg_os + .to_string_lossy() .into_owned(), cdate, self.meta @@ -955,7 +970,8 @@ impl<'a, 'b> Parser<'a, 'b> None), self.color())); } else { - return Err(Error::unrecognized_subcommand(arg_os.to_string_lossy() + return Err(Error::unrecognized_subcommand(arg_os + .to_string_lossy() .into_owned(), self.meta .bin_name @@ -977,10 +993,7 @@ impl<'a, 'b> Parser<'a, 'b> }; try!(self.parse_subcommand(&*sc_name, matcher, it)); } else if self.is_set(AS::SubcommandRequired) { - let bn = self.meta - .bin_name - .as_ref() - .unwrap_or(&self.meta.name); + let bn = self.meta.bin_name.as_ref().unwrap_or(&self.meta.name); return Err(Error::missing_subcommand(bn, &usage::create_error_usage(self, matcher, None), self.color())); @@ -1011,10 +1024,7 @@ impl<'a, 'b> Parser<'a, 'b> debugln!("Parser::build_bin_names;"); for sc in &mut self.subcommands { debug!("Parser::build_bin_names:iter: bin_name set..."); - if sc.p - .meta - .bin_name - .is_none() { + if sc.p.meta.bin_name.is_none() { sdebugln!("No"); let bin_name = format!("{}{}{}", self.meta @@ -1052,10 +1062,7 @@ impl<'a, 'b> Parser<'a, 'b> debugln!("Parser::parse_subcommand;"); let mut mid_string = String::new(); if !self.is_set(AS::SubcommandsNegateReqs) { - let mut hs: Vec<&str> = self.required - .iter() - .map(|n| &**n) - .collect(); + let mut hs: Vec<&str> = self.required.iter().map(|n| &**n).collect(); for k in matcher.arg_names() { hs.push(k); } @@ -1066,41 +1073,35 @@ impl<'a, 'b> Parser<'a, 'b> } } mid_string.push_str(" "); - if let Some(ref mut sc) = self.subcommands.iter_mut().find(|s| &s.p.meta.name == &sc_name) { + if let Some(ref mut sc) = self.subcommands + .iter_mut() + .find(|s| &s.p.meta.name == &sc_name) { let mut sc_matcher = ArgMatcher::new(); // bin_name should be parent's bin_name + [] + the sc's name separated by // a space sc.p.meta.usage = Some(format!("{}{}{}", - self.meta - .bin_name - .as_ref() - .unwrap_or(&String::new()), + self.meta.bin_name.as_ref().unwrap_or(&String::new()), if self.meta.bin_name.is_some() { &*mid_string } else { "" }, &*sc.p.meta.name)); - sc.p.meta.bin_name = Some(format!("{}{}{}", - self.meta - .bin_name - .as_ref() - .unwrap_or(&String::new()), - if self.meta.bin_name.is_some() { - " " - } else { - "" - }, - &*sc.p.meta.name)); + sc.p.meta.bin_name = + Some(format!("{}{}{}", + self.meta.bin_name.as_ref().unwrap_or(&String::new()), + if self.meta.bin_name.is_some() { + " " + } else { + "" + }, + &*sc.p.meta.name)); debugln!("Parser::parse_subcommand: About to parse sc={}", sc.p.meta.name); debugln!("Parser::parse_subcommand: sc settings={:#?}", sc.p.settings); try!(sc.p.get_matches_with(&mut sc_matcher, it)); matcher.subcommand(SubCommand { - name: sc.p - .meta - .name - .clone(), + name: sc.p.meta.name.clone(), matches: sc_matcher.into(), }); } @@ -1211,7 +1212,8 @@ impl<'a, 'b> Parser<'a, 'b> let arg = FlagBuilder { b: Base { name: "vclap_version", - help: self.version_message.or(Some("Prints version information")), + help: self.version_message + .or(Some("Prints version information")), ..Default::default() }, s: Switched { @@ -1239,11 +1241,11 @@ impl<'a, 'b> Parser<'a, 'b> arg.to_str().unwrap()); if arg == "help" && self.is_set(AS::NeedsLongHelp) { sdebugln!("Help"); - try!(self._help()); + try!(self._help(true)); } if arg == "version" && self.is_set(AS::NeedsLongVersion) { sdebugln!("Version"); - try!(self._version()); + try!(self._version(true)); } sdebugln!("Neither"); @@ -1257,22 +1259,37 @@ impl<'a, 'b> Parser<'a, 'b> if let Some(h) = self.help_short { if arg == h && self.is_set(AS::NeedsLongHelp) { sdebugln!("Help"); - try!(self._help()); + try!(self._help(false)); } } if let Some(v) = self.version_short { if arg == v && self.is_set(AS::NeedsLongVersion) { sdebugln!("Version"); - try!(self._version()); + try!(self._version(false)); } } sdebugln!("Neither"); Ok(()) } - fn _help(&self) -> ClapResult<()> { + fn use_long_help(&self) -> bool { + let ul = self.flags.iter().any(|f| f.b.long_help.is_some()) || + self.opts.iter().any(|o| o.b.long_help.is_some()) || + self.positionals + .values() + .any(|p| p.b.long_help.is_some()) || + self.subcommands + .iter() + .any(|s| s.p.meta.long_about.is_some()); + debugln!("Parser::use_long_help: ret={:?}", ul); + ul + } + + fn _help(&self, mut use_long: bool) -> ClapResult<()> { + debugln!("Parser::_help: use_long={:?}", use_long); + use_long = use_long && self.use_long_help(); let mut buf = vec![]; - try!(Help::write_parser_help(&mut buf, self)); + try!(Help::write_parser_help(&mut buf, self, use_long)); Err(Error { message: unsafe { String::from_utf8_unchecked(buf) }, kind: ErrorKind::HelpDisplayed, @@ -1280,10 +1297,11 @@ impl<'a, 'b> Parser<'a, 'b> }) } - fn _version(&self) -> ClapResult<()> { + fn _version(&self, use_long: bool) -> ClapResult<()> { + debugln!("Parser::_version: "); let out = io::stdout(); let mut buf_w = BufWriter::new(out.lock()); - try!(self.print_version(&mut buf_w)); + try!(self.print_version(&mut buf_w, use_long)); Err(Error { message: String::new(), kind: ErrorKind::VersionDisplayed, @@ -1344,7 +1362,8 @@ impl<'a, 'b> Parser<'a, 'b> } debugln!("Parser::parse_long_arg: Didn't match anything"); - self.did_you_mean_error(arg.to_str().expect(INVALID_UTF8), matcher).map(|_| None) + self.did_you_mean_error(arg.to_str().expect(INVALID_UTF8), matcher) + .map(|_| None) } #[cfg_attr(feature = "lints", allow(len_zero))] @@ -1470,7 +1489,8 @@ impl<'a, 'b> Parser<'a, 'b> matcher.inc_occurrence_of(opt.b.name); // Increment or create the group "args" - self.groups_for_arg(opt.b.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 && @@ -1556,7 +1576,8 @@ impl<'a, 'b> Parser<'a, 'b> matcher.inc_occurrence_of(flag.b.name); // Increment or create the group "args" - self.groups_for_arg(flag.b.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(()) } @@ -1589,30 +1610,30 @@ impl<'a, 'b> Parser<'a, 'b> } // Prints the version to the user and exits if quit=true - fn print_version(&self, w: &mut W) -> ClapResult<()> { - try!(self.write_version(w)); + fn print_version(&self, w: &mut W, use_long: bool) -> ClapResult<()> { + try!(self.write_version(w, use_long)); w.flush().map_err(Error::from) } - pub fn write_version(&self, w: &mut W) -> io::Result<()> { + pub fn write_version(&self, w: &mut W, use_long: bool) -> io::Result<()> { + let ver = if use_long { + self.meta + .long_version + .unwrap_or(self.meta.version.unwrap_or("".into())) + } else { + self.meta + .version + .unwrap_or(self.meta.long_version.unwrap_or("".into())) + }; if let Some(bn) = self.meta.bin_name.as_ref() { if bn.contains(' ') { // Incase we're dealing with subcommands i.e. git mv is translated to git-mv - write!(w, - "{} {}", - bn.replace(" ", "-"), - self.meta.version.unwrap_or("".into())) + write!(w, "{} {}", bn.replace(" ", "-"), ver) } else { - write!(w, - "{} {}", - &self.meta.name[..], - self.meta.version.unwrap_or("".into())) + write!(w, "{} {}", &self.meta.name[..], ver) } } else { - write!(w, - "{} {}", - &self.meta.name[..], - self.meta.version.unwrap_or("".into())) + write!(w, "{} {}", &self.meta.name[..], ver) } } @@ -1623,7 +1644,11 @@ impl<'a, 'b> Parser<'a, 'b> } pub fn write_help(&self, w: &mut W) -> ClapResult<()> { - Help::write_parser_help(w, self) + Help::write_parser_help(w, self, false) + } + + pub fn write_long_help(&self, w: &mut W) -> ClapResult<()> { + Help::write_parser_help(w, self, true) } pub fn write_help_err(&self, w: &mut W) -> ClapResult<()> { @@ -1730,20 +1755,10 @@ impl<'a, 'b> Parser<'a, 'b> pub fn find_subcommand(&'b self, sc: &str) -> Option<&'b App<'a, 'b>> { debugln!("Parser::find_subcommand: sc={}", sc); debugln!("Parser::find_subcommand: Currently in Parser...{}", - self.meta - .bin_name - .as_ref() - .unwrap()); + self.meta.bin_name.as_ref().unwrap()); for s in self.subcommands.iter() { - if s.p - .meta - .bin_name - .as_ref() - .unwrap_or(&String::new()) == sc || - (s.p - .meta - .aliases - .is_some() && + if s.p.meta.bin_name.as_ref().unwrap_or(&String::new()) == sc || + (s.p.meta.aliases.is_some() && s.p .meta .aliases @@ -1751,12 +1766,8 @@ impl<'a, 'b> Parser<'a, 'b> .unwrap() .iter() .any(|&(s, _)| { - s == - sc.split(' ') - .rev() - .next() - .expect(INTERNAL_ERROR_MSG) - })) { + s == sc.split(' ').rev().next().expect(INTERNAL_ERROR_MSG) + })) { return Some(s); } if let Some(app) = s.p.find_subcommand(sc) { diff --git a/src/args/any_arg.rs b/src/args/any_arg.rs index 55245dba..5f8615fc 100644 --- a/src/args/any_arg.rs +++ b/src/args/any_arg.rs @@ -32,6 +32,7 @@ pub trait AnyArg<'n, 'e>: std_fmt::Display { fn takes_value(&self) -> bool; fn val_names(&self) -> Option<&VecMap<&'e str>>; fn help(&self) -> Option<&'e str>; + fn long_help(&self) -> Option<&'e str>; fn default_val(&self) -> Option<&'e OsStr>; fn default_vals_ifs(&self) -> Option, &'e OsStr)>>; fn longest_filter(&self) -> bool; diff --git a/src/args/arg.rs b/src/args/arg.rs index 9f57a883..860477c6 100644 --- a/src/args/arg.rs +++ b/src/args/arg.rs @@ -103,6 +103,7 @@ impl<'a, 'b> Arg<'a, 'b> { "long" => yaml_to_str!(a, v, long), "aliases" => yaml_vec_or_str!(v, a, alias), "help" => yaml_to_str!(a, v, help), + "long_help" => yaml_to_str!(a, v, long_help), "required" => yaml_to_bool!(a, v, required), "required_if" => yaml_tuple2!(a, v, required_if), "required_ifs" => yaml_tuple2!(a, v, required_if), @@ -489,8 +490,14 @@ impl<'a, 'b> Arg<'a, 'b> { self } - /// Sets the help text of the argument that will be displayed to the user when they print the - /// usage/help information. + /// Sets the short help text of the argument that will be displayed to the user when they print + /// the help information with `-h`. Typically, this is a short (one line) description of the + /// arg. + /// + /// **NOTE:** If only `Arg::help` is provided, and not [`Arg::long_help`] but the user requests + /// `--help` clap will still display the contents of `help` appropriately + /// + /// **NOTE:** Only `Arg::help` is used in completion script generation in order to be concise /// /// # Examples /// @@ -532,11 +539,83 @@ impl<'a, 'b> Arg<'a, 'b> { /// -h, --help Prints help information /// -V, --version Prints version information /// ``` + /// [`Arg::long_help`]: ./struct.Arg.html#method.long_help pub fn help(mut self, h: &'b str) -> Self { self.b.help = Some(h); self } + /// Sets the long help text of the argument that will be displayed to the user when they print + /// the help information with `--help`. Typically this a more detailed (multi-line) message + /// that describes the arg. + /// + /// **NOTE:** If only `long_help` is provided, and not [`Arg::help`] but the user requests `-h` + /// clap will still display the contents of `long_help` appropriately + /// + /// **NOTE:** Only [`Arg::help`] is used in completion script generation in order to be concise + /// + /// # Examples + /// + /// Any valid UTF-8 is allowed in the help text. The one exception is when one wishes to + /// include a newline in the help text and have the following text be properly aligned with all + /// the other help text. + /// + /// ```rust + /// # use clap::{App, Arg}; + /// Arg::with_name("config") + /// .long_help( + /// "The config file used by the myprog must be in JSON format + /// with only valid keys and may not contain other nonsense + /// that cannot be read by this program. Obviously I'm going on + /// and on, so I'll stop now.") + /// # ; + /// ``` + /// + /// Setting `help` displays a short message to the side of the argument when the user passes + /// `-h` or `--help` (by default). + /// + /// ```rust + /// # use clap::{App, Arg}; + /// let m = App::new("prog") + /// .arg(Arg::with_name("cfg") + /// .long("config") + /// .long_help( + /// "The config file used by the myprog must be in JSON format + /// with only valid keys and may not contain other nonsense + /// that cannot be read by this program. Obviously I'm going on + /// and on, so I'll stop now.")) + /// .get_matches_from(vec![ + /// "prog", "--help" + /// ]); + /// ``` + /// + /// The above example displays + /// + /// ```notrust + /// helptest + /// + /// USAGE: + /// helptest [FLAGS] + /// + /// FLAGS: + /// --config + /// The config file used by the myprog must be in JSON format + /// with only valid keys and may not contain other nonsense + /// that cannot be read by this program. Obviously I'm going on + /// and on, so I'll stop now. + /// + /// -h, --help + /// Prints help information + /// + /// -V, --version + /// Prints version information + /// ``` + /// [`Arg::help`]: ./struct.Arg.html#method.help + pub fn long_help(mut self, h: &'b str) -> Self { + self.b.long_help = Some(h); + self + } + /// Specifies that this arg is the last, or final, positional argument (i.e. has the highest /// index) and is *only* able to be accessed via the `--` syntax (i.e. `$ prog args -- /// last_arg`). Even, if no other arguments are left to parse, if the user omits the `--` syntax diff --git a/src/args/arg_builder/base.rs b/src/args/arg_builder/base.rs index 8b566e38..5990bc08 100644 --- a/src/args/arg_builder/base.rs +++ b/src/args/arg_builder/base.rs @@ -7,6 +7,7 @@ pub struct Base<'a, 'b> { pub name: &'a str, pub help: Option<&'b str>, + pub long_help: Option<&'b str>, pub blacklist: Option>, pub settings: ArgFlags, pub r_unless: Option>, diff --git a/src/args/arg_builder/flag.rs b/src/args/arg_builder/flag.rs index d1c16690..e3e9dd01 100644 --- a/src/args/arg_builder/flag.rs +++ b/src/args/arg_builder/flag.rs @@ -79,6 +79,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> { 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 long_help(&self) -> Option<&'e str> { self.b.long_help } fn val_terminator(&self) -> Option<&'e str> { None } fn default_val(&self) -> Option<&'e OsStr> { None } fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { diff --git a/src/args/arg_builder/option.rs b/src/args/arg_builder/option.rs index 342ccbdc..64dd9c46 100644 --- a/src/args/arg_builder/option.rs +++ b/src/args/arg_builder/option.rs @@ -129,6 +129,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> { 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 long_help(&self) -> Option<&'e str> { self.b.long_help } fn default_val(&self) -> Option<&'e OsStr> { self.v.default_val } fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { self.v.default_vals_ifs.as_ref().map(|vm| vm.values()) diff --git a/src/args/arg_builder/positional.rs b/src/args/arg_builder/positional.rs index ddbd8964..c77976b1 100644 --- a/src/args/arg_builder/positional.rs +++ b/src/args/arg_builder/positional.rs @@ -126,6 +126,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> { 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 long_help(&self) -> Option<&'e str> { self.b.long_help } fn default_vals_ifs(&self) -> Option, &'e OsStr)>> { self.v.default_vals_ifs.as_ref().map(|vm| vm.values()) } diff --git a/src/macros.rs b/src/macros.rs index 6fc7763a..650042fb 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -562,6 +562,8 @@ macro_rules! app_from_crate { /// /// * A single hyphen followed by a character (such as `-c`) sets the [`Arg::short`] /// * A double hyphen followed by a character or word (such as `--config`) sets [`Arg::long`] +/// * If one wishes to use a [`Arg::long`] with a hyphen inside (i.e. `--config-file`), you +/// must use `--("config-file")` due to limitations of the Rust macro system. /// * Three dots (`...`) sets [`Arg::multiple(true)`] /// * Angled brackets after either a short or long will set [`Arg::value_name`] and /// `Arg::required(true)` such as `--config ` = `Arg::value_name("FILE")` and @@ -595,6 +597,13 @@ macro_rules! app_from_crate { #[macro_export] macro_rules! clap_app { (@app ($builder:expr)) => { $builder }; + (@app ($builder:expr) (@arg ($name:expr): $($tail:tt)*) $($tt:tt)*) => { + clap_app!{ @app + ($builder.arg( + clap_app!{ @arg ($crate::Arg::with_name($name)) (-) $($tail)* })) + $($tt)* + } + }; (@app ($builder:expr) (@arg $name:ident: $($tail:tt)*) $($tt:tt)*) => { clap_app!{ @app ($builder.arg( diff --git a/tests/help.rs b/tests/help.rs index 3f273931..89ed7ec6 100644 --- a/tests/help.rs +++ b/tests/help.rs @@ -831,3 +831,16 @@ fn hidden_default_val() { .default_value("default-argument")); assert!(test::compare_output(app2, "default --help", HIDE_DEFAULT_VAL, false)); } + +#[test] +fn override_help() { + let m = App::new("test") + .author("Kevin K.") + .about("tests stuff") + .version("1.3") + .arg(Arg::from_usage("-H, --help 'some help'")) + .get_matches_from_safe(vec!["test", "--help"]); + + assert!(m.is_ok()); + assert!(m.unwrap().is_present("help")); +} \ No newline at end of file diff --git a/tests/macros.rs b/tests/macros.rs index cc25e1bf..035983bb 100644 --- a/tests/macros.rs +++ b/tests/macros.rs @@ -110,3 +110,40 @@ fn quoted_arg_long_name() { .expect("Expected to successfully match the given args."); assert!(matches.is_present("option2")); } + +#[test] +fn quoted_arg_name() { + let app = clap_app!(claptests => + (version: "0.1") + (about: "tests clap library") + (author: "Kevin K. ") + (@arg opt: -o --option +takes_value ... "tests options") + (@arg ("positional-arg"): index(1) "tests positionals") + (@arg flag: -f --flag ... +global "tests flags") + (@arg flag2: -F conflicts_with[flag] requires[option2] + "tests flags with exclusions") + (@arg option2: --("long-option-2") conflicts_with[option] requires[positional2] + "tests long options with exclusions") + (@arg positional2: index(2) "tests positionals with exclusions") + (@arg option3: -O --Option +takes_value possible_value[fast slow] + "tests options with specific value sets") + (@arg ("positional-3"): index(3) ... possible_value[vi emacs] + "tests positionals with specific values") + (@arg multvals: --multvals +takes_value value_name[one two] + "Tests mutliple values, not mult occs") + (@arg multvalsmo: --multvalsmo ... +takes_value value_name[one two] + "Tests mutliple values, not mult occs") + (@arg minvals: --minvals2 min_values(1) ... +takes_value "Tests 2 min vals") + (@arg maxvals: --maxvals3 ... +takes_value max_values(3) "Tests 3 max vals") + (@subcommand subcmd => + (about: "tests subcommands") + (version: "0.1") + (author: "Kevin K. ") + (@arg scoption: -o --option ... +takes_value "tests options") + (@arg scpositional: index(1) "tests positionals")) + ); + + let matches = app.get_matches_from_safe(vec!["bin_name", "value1", "value2", "--long-option-2"]) + .expect("Expected to successfully match the given args."); + assert!(matches.is_present("option2")); +} diff --git a/tests/version.rs b/tests/version.rs index f6e9ff8b..de0f8179 100644 --- a/tests/version.rs +++ b/tests/version.rs @@ -3,7 +3,7 @@ extern crate regex; use std::str; -use clap::{App, ErrorKind}; +use clap::{App, Arg, ErrorKind}; include!("../clap-test.rs"); @@ -43,3 +43,16 @@ fn complex_version_output() { a.write_version(&mut ver).unwrap(); assert_eq!(str::from_utf8(&ver).unwrap(), VERSION); } + +#[test] +fn override_ver() { + let m = App::new("test") + .author("Kevin K.") + .about("tests stuff") + .version("1.3") + .arg(Arg::from_usage("-v, --version 'some version'")) + .get_matches_from_safe(vec!["test", "--version"]); + + assert!(m.is_ok()); + assert!(m.unwrap().is_present("version")); +} \ No newline at end of file