Auto merge of #923 - kbknapp:issues-869,900,919,922, r=kbknapp

Issues 869,900,919,922

<!-- Reviewable:start -->
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/kbknapp/clap-rs/923)
<!-- Reviewable:end -->
This commit is contained in:
Homu 2017-04-05 21:57:49 +09:00
commit 5b669ec520
17 changed files with 571 additions and 219 deletions

View file

@ -1,3 +1,34 @@
<a name="v2.23.0"></a>
## 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))
<a name="v2.22.2"></a> <a name="v2.22.2"></a>
### v2.22.2 (2017-03-30) ### v2.22.2 (2017-03-30)

View file

@ -1,7 +1,7 @@
[package] [package]
name = "clap" name = "clap"
version = "2.22.2" version = "2.23.0"
authors = ["Kevin K. <kbknapp@gmail.com>"] authors = ["Kevin K. <kbknapp@gmail.com>"]
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"] exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
repository = "https://github.com/kbknapp/clap-rs.git" repository = "https://github.com/kbknapp/clap-rs.git"

View file

@ -45,12 +45,24 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
## What's New ## 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 * 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 * fixes a big regression with custom usage strings
* adds the ability to change the name of the App instance after creation * 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 * adds ability to hide the default value of an argument from the help string

View file

@ -89,6 +89,7 @@ pub struct Help<'a> {
cizer: Colorizer, cizer: Colorizer,
longest: usize, longest: usize,
force_next_line: bool, force_next_line: bool,
use_long: bool,
} }
// Public Functions // Public Functions
@ -100,7 +101,8 @@ impl<'a> Help<'a> {
color: bool, color: bool,
cizer: Colorizer, cizer: Colorizer,
term_w: Option<usize>, term_w: Option<usize>,
max_w: Option<usize>) max_w: Option<usize>,
use_long: bool)
-> Self { -> Self {
debugln!("Help::new;"); debugln!("Help::new;");
Help { Help {
@ -121,21 +123,22 @@ impl<'a> Help<'a> {
cizer: cizer, cizer: cizer,
longest: 0, longest: 0,
force_next_line: false, force_next_line: false,
use_long: use_long,
} }
} }
/// Reads help settings from an App /// Reads help settings from an App
/// and write its help to the wrapped stream. /// 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;"); 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 /// Reads help settings from a Parser
/// and write its help to the wrapped stream. /// 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;"); 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 /// Reads help settings from a Parser
@ -143,11 +146,11 @@ impl<'a> Help<'a> {
/// formatting when required. /// formatting when required.
pub fn write_parser_help_to_stderr(w: &'a mut Write, parser: &Parser) -> ClapResult<()> { pub fn write_parser_help_to_stderr(w: &'a mut Write, parser: &Parser) -> ClapResult<()> {
debugln!("Help::write_parser_help;"); debugln!("Help::write_parser_help;");
Self::_write_parser_help(w, parser, true) Self::_write_parser_help(w, parser, true, false)
} }
#[doc(hidden)] #[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;"); debugln!("Help::write_parser_help;");
let nlh = parser.is_set(AppSettings::NextLineHelp); let nlh = parser.is_set(AppSettings::NextLineHelp);
let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp); let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp);
@ -162,7 +165,8 @@ impl<'a> Help<'a> {
color, color,
cizer, cizer,
parser.meta.term_w, parser.meta.term_w,
parser.meta.max_w) parser.meta.max_w,
use_long)
.write_help(parser) .write_help(parser)
} }
@ -191,7 +195,8 @@ impl<'a> Help<'a> {
self.longest = 2; self.longest = 2;
let mut arg_v = Vec::with_capacity(10); let mut arg_v = Vec::with_capacity(10);
for arg in args.filter(|arg| { 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() { if arg.longest_filter() {
self.longest = cmp::max(self.longest, arg.to_string().len()); 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<()> { fn help<'b, 'c>(&mut self, arg: &ArgWithDisplay<'b, 'c>, spec_vals: &str) -> io::Result<()> {
debugln!("Help::help;"); debugln!("Help::help;");
let mut help = String::new(); let mut help = String::new();
let h = arg.help().unwrap_or(""); let h = if self.use_long {
let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp); 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); debugln!("Help::help: Next Line...{:?}", nlh);
let spcs = if nlh || self.force_next_line { let spcs = if nlh || self.force_next_line {
@ -513,7 +522,8 @@ impl<'a> Help<'a> {
debugln!("Help::spec_vals: Found aliases...{:?}", aliases); debugln!("Help::spec_vals: Found aliases...{:?}", aliases);
spec_vals.push(format!(" [aliases: {}]", spec_vals.push(format!(" [aliases: {}]",
if self.color { if self.color {
aliases.iter() aliases
.iter()
.map(|v| format!("{}", self.cizer.good(v))) .map(|v| format!("{}", self.cizer.good(v)))
.collect::<Vec<_>>() .collect::<Vec<_>>()
.join(", ") .join(", ")
@ -548,7 +558,10 @@ impl<'a> Help<'a> {
pub fn write_all_args(&mut self, parser: &Parser) -> ClapResult<()> { pub fn write_all_args(&mut self, parser: &Parser) -> ClapResult<()> {
debugln!("Help::write_all_args;"); debugln!("Help::write_all_args;");
let flags = parser.has_flags(); 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 opts = parser.has_opts();
let subcmds = parser.has_subcommands(); let subcmds = parser.has_subcommands();
@ -557,7 +570,8 @@ impl<'a> Help<'a> {
let mut first = true; let mut first = true;
if unified_help && (flags || opts) { if unified_help && (flags || opts) {
let opts_flags = parser.flags() let opts_flags = parser
.flags()
.map(as_arg_trait) .map(as_arg_trait)
.chain(parser.opts().map(as_arg_trait)); .chain(parser.opts().map(as_arg_trait));
try!(color!(self, "OPTIONS:\n", warning)); try!(color!(self, "OPTIONS:\n", warning));
@ -566,8 +580,7 @@ impl<'a> Help<'a> {
} else { } else {
if flags { if flags {
try!(color!(self, "FLAGS:\n", warning)); try!(color!(self, "FLAGS:\n", warning));
try!(self.write_args(parser.flags() try!(self.write_args(parser.flags().map(as_arg_trait)));
.map(as_arg_trait)));
first = false; first = false;
} }
if opts { if opts {
@ -606,8 +619,13 @@ impl<'a> Help<'a> {
// The shortest an arg can legally be is 2 (i.e. '-x') // The shortest an arg can legally be is 2 (i.e. '-x')
self.longest = 2; self.longest = 2;
let mut ord_m = VecMap::new(); let mut ord_m = VecMap::new();
for sc in parser.subcommands.iter().filter(|s| !s.p.is_set(AppSettings::Hidden)) { for sc in parser
let btm = ord_m.entry(sc.p.meta.disp_ord).or_insert(BTreeMap::new()); .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()); self.longest = cmp::max(self.longest, sc.p.meta.name.len());
btm.insert(sc.p.meta.name.clone(), sc.clone()); btm.insert(sc.p.meta.name.clone(), sc.clone());
} }
@ -894,22 +912,20 @@ impl<'a> Help<'a> {
try!(self.write_all_args(&parser)); try!(self.write_all_args(&parser));
} }
b"unified" => { b"unified" => {
let opts_flags = parser.flags() let opts_flags = parser
.flags()
.map(as_arg_trait) .map(as_arg_trait)
.chain(parser.opts().map(as_arg_trait)); .chain(parser.opts().map(as_arg_trait));
try!(self.write_args(opts_flags)); try!(self.write_args(opts_flags));
} }
b"flags" => { b"flags" => {
try!(self.write_args(parser.flags() try!(self.write_args(parser.flags().map(as_arg_trait)));
.map(as_arg_trait)));
} }
b"options" => { b"options" => {
try!(self.write_args(parser.opts() try!(self.write_args(parser.opts().map(as_arg_trait)));
.map(as_arg_trait)));
} }
b"positionals" => { b"positionals" => {
try!(self.write_args(parser.positionals() try!(self.write_args(parser.positionals().map(as_arg_trait)));
.map(as_arg_trait)));
} }
b"subcommands" => { b"subcommands" => {
try!(self.write_subcommands(&parser)); try!(self.write_subcommands(&parser));

View file

@ -1,11 +1,14 @@
#[doc(hidden)] #[doc(hidden)]
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
#[derive(Default, Clone)]
pub struct AppMeta<'b> { pub struct AppMeta<'b> {
pub name: String, pub name: String,
pub bin_name: Option<String>, pub bin_name: Option<String>,
pub author: Option<&'b str>, pub author: Option<&'b str>,
pub version: Option<&'b str>, pub version: Option<&'b str>,
pub long_version: Option<&'b str>,
pub about: Option<&'b str>, pub about: Option<&'b str>,
pub long_about: Option<&'b str>,
pub more_help: Option<&'b str>, pub more_help: Option<&'b str>,
pub pre_help: Option<&'b str>, pub pre_help: Option<&'b str>,
pub aliases: Option<Vec<(&'b str, bool)>>, // (name, visible) pub aliases: Option<Vec<(&'b str, bool)>>, // (name, visible)
@ -18,51 +21,7 @@ pub struct AppMeta<'b> {
pub template: Option<&'b str>, 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> { impl<'b> AppMeta<'b> {
pub fn new() -> Self { Default::default() } pub fn new() -> Self { Default::default() }
pub fn with_name(s: String) -> Self { AppMeta { name: s, ..Default::default() } } pub fn with_name(s: String) -> Self { AppMeta { name: s, disp_ord: 999, ..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,
}
}
} }

View file

@ -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 /// 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 /// # Examples
/// ///
@ -210,11 +216,38 @@ impl<'a, 'b> App<'a, 'b> {
/// .about("Does really amazing things to great people") /// .about("Does really amazing things to great people")
/// # ; /// # ;
/// ``` /// ```
/// [`App::long_about`]: ./struct.App.html#method.long_about
pub fn about<S: Into<&'b str>>(mut self, about: S) -> Self { pub fn about<S: Into<&'b str>>(mut self, about: S) -> Self {
self.p.meta.about = Some(about.into()); self.p.meta.about = Some(about.into());
self 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<S: Into<&'b str>>(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. /// 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 /// **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 /// 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 /// **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/`] /// 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 /// [`crate_version!`]: ./macro.crate_version!.html
/// [`examples/`]: https://github.com/kbknapp/clap-rs/tree/master/examples /// [`examples/`]: https://github.com/kbknapp/clap-rs/tree/master/examples
/// [`App::long_version`]: ./struct.App.html#method.long_version
pub fn version<S: Into<&'b str>>(mut self, ver: S) -> Self { pub fn version<S: Into<&'b str>>(mut self, ver: S) -> Self {
self.p.meta.version = Some(ver.into()); self.p.meta.version = Some(ver.into());
self 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<S: Into<&'b str>>(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. /// 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 /// 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 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 /// # Examples
/// ///
@ -1073,6 +1145,8 @@ impl<'a, 'b> App<'a, 'b> {
/// ``` /// ```
/// [`io::stdout()`]: https://doc.rust-lang.org/std/io/fn.stdout.html /// [`io::stdout()`]: https://doc.rust-lang.org/std/io/fn.stdout.html
/// [`BufWriter`]: https://doc.rust-lang.org/std/io/struct.BufWriter.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<()> { pub fn print_help(&mut self) -> ClapResult<()> {
// If there are global arguments, or settings we need to propgate them down to subcommands // If there are global arguments, or settings we need to propgate them down to subcommands
// before parsing incase we run into a subcommand // before parsing incase we run into a subcommand
@ -1086,7 +1160,45 @@ impl<'a, 'b> App<'a, 'b> {
self.write_help(&mut buf_w) 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 /// # Examples
/// ///
@ -1098,6 +1210,8 @@ impl<'a, 'b> App<'a, 'b> {
/// app.write_help(&mut out).expect("failed to write to stdout"); /// app.write_help(&mut out).expect("failed to write to stdout");
/// ``` /// ```
/// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html /// [`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<W: Write>(&self, w: &mut W) -> ClapResult<()> { pub fn write_help<W: Write>(&self, w: &mut W) -> ClapResult<()> {
// PENDING ISSUE: 808 // PENDING ISSUE: 808
// https://github.com/kbknapp/clap-rs/issues/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.derive_display_order();
// self.p.create_help_and_version(); // 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<W: Write>(&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 /// # Examples
/// ///
@ -1123,10 +1267,32 @@ impl<'a, 'b> App<'a, 'b> {
/// app.write_version(&mut out).expect("failed to write to stdout"); /// app.write_version(&mut out).expect("failed to write to stdout");
/// ``` /// ```
/// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html /// [`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<W: Write>(&self, w: &mut W) -> ClapResult<()> { pub fn write_version<W: Write>(&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<W: Write>(&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. /// 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 = "build.rs"
/// ///
/// [build-dependencies] /// [build-dependencies]
/// clap = "2.9" /// clap = "2.23"
/// ``` /// ```
/// ///
/// Next, we place a `build.rs` in our project root. /// 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<char> { None } fn val_delim(&self) -> Option<char> { None }
fn takes_value(&self) -> bool { true } fn takes_value(&self) -> bool { true }
fn help(&self) -> Option<&'e str> { self.p.meta.about } 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_val(&self) -> Option<&'e OsStr> { None }
fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> { fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
None None

View file

@ -63,7 +63,10 @@ impl<'a, 'b> Parser<'a, 'b>
where 'a: 'b where 'a: 'b
{ {
pub fn with_name(n: String) -> Self { 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) { pub fn help_short(&mut self, s: &str) {
@ -98,11 +101,7 @@ impl<'a, 'b> Parser<'a, 'b>
use std::error::Error; use std::error::Error;
let out_dir = PathBuf::from(od); let out_dir = PathBuf::from(od);
let name = &*self.meta let name = &*self.meta.bin_name.as_ref().unwrap().clone();
.bin_name
.as_ref()
.unwrap()
.clone();
let file_name = match for_shell { let file_name = match for_shell {
Shell::Bash => format!("{}.bash-completion", name), Shell::Bash => format!("{}.bash-completion", name),
Shell::Fish => format!("{}.fish", name), Shell::Fish => format!("{}.fish", name),
@ -146,7 +145,9 @@ impl<'a, 'b> Parser<'a, 'b>
global and required", global and required",
a.b.name); a.b.name);
if a.b.is_set(ArgSettings::Last) { 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."); "Only one positional argument may have last(true) set. Found two.");
assert!(a.s.long.is_none(), assert!(a.s.long.is_none(),
"Flags or Options may not have last(true) set. {} has both a long and last(true) set.", "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 a.is_set(ArgSettings::Required) {
// If the arg is required, add all it's requirements to master required list // If the arg is required, add all it's requirements to master required list
if let Some(ref areqs) = a.b.requires { 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); self.required.push(name);
} }
} }
@ -206,6 +210,13 @@ impl<'a, 'b> Parser<'a, 'b>
self.set(AS::DontCollapseArgsInUsage); self.set(AS::DontCollapseArgsInUsage);
self.set(AS::ContainsLast); 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 // actually adds the arguments
@ -225,7 +236,8 @@ impl<'a, 'b> Parser<'a, 'b>
} else { } else {
a.index.unwrap() as usize 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) { } else if a.is_set(ArgSettings::TakesValue) {
let mut ob = OptBuilder::from(a); let mut ob = OptBuilder::from(a);
ob.s.unified_ord = self.flags.len() + self.opts.len(); ob.s.unified_ord = self.flags.len() + self.opts.len();
@ -319,11 +331,7 @@ impl<'a, 'b> Parser<'a, 'b>
if vsc { if vsc {
sc.p.set(AS::DisableVersion); sc.p.set(AS::DisableVersion);
} }
if gv && if gv && sc.p.meta.version.is_none() && self.meta.version.is_some() {
sc.p
.meta
.version
.is_none() && self.meta.version.is_some() {
sc.p.set(AS::GlobalVersion); sc.p.set(AS::GlobalVersion);
sc.p.meta.version = Some(self.meta.version.unwrap()); sc.p.meta.version = Some(self.meta.version.unwrap());
} }
@ -397,7 +405,9 @@ impl<'a, 'b> Parser<'a, 'b>
if self.flags.is_empty() { if self.flags.is_empty() {
return false; return false;
} }
self.flags.iter().any(|f| !f.is_set(ArgSettings::Hidden)) self.flags
.iter()
.any(|f| !f.is_set(ArgSettings::Hidden))
} }
#[inline] #[inline]
@ -405,7 +415,9 @@ impl<'a, 'b> Parser<'a, 'b>
if self.positionals.is_empty() { if self.positionals.is_empty() {
return false; return false;
} }
self.positionals.values().any(|p| !p.is_set(ArgSettings::Hidden)) self.positionals
.values()
.any(|p| !p.is_set(ArgSettings::Hidden))
} }
#[inline] #[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 // 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 // positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3
// but no 2) // but no 2)
if let Some((idx, p)) = self.positionals if let Some((idx, p)) = self.positionals.iter().rev().next() {
.iter()
.rev()
.next() {
assert!(!(idx != self.positionals.len()), assert!(!(idx != self.positionals.len()),
"Found positional argument \"{}\" who's index is {} but there \ "Found positional argument \"{}\" who's index is {} but there \
are only {} positional arguments defined", are only {} positional arguments defined",
@ -446,7 +455,9 @@ impl<'a, 'b> Parser<'a, 'b>
} }
// Next we verify that only the highest index has a .multiple(true) (if any) // Next we verify that only the highest index has a .multiple(true) (if any)
if self.positionals.values().any(|a| { if self.positionals
.values()
.any(|a| {
a.b.is_set(ArgSettings::Multiple) && a.b.is_set(ArgSettings::Multiple) &&
(a.index as usize != self.positionals.len()) (a.index as usize != self.positionals.len())
}) { }) {
@ -535,9 +546,10 @@ impl<'a, 'b> Parser<'a, 'b>
} }
} }
} }
if self.positionals.values().any(|p| { if self.positionals
p.b.is_set(ArgSettings::Last) && .values()
p.b.is_set(ArgSettings::Required) .any(|p| {
p.b.is_set(ArgSettings::Last) && p.b.is_set(ArgSettings::Required)
}) && self.has_subcommands() && }) && self.has_subcommands() &&
!self.is_set(AS::SubcommandsNegateReqs) { !self.is_set(AS::SubcommandsNegateReqs) {
panic!("Having a required positional argument with .last(true) set *and* child \ panic!("Having a required positional argument with .last(true) set *and* child \
@ -587,10 +599,7 @@ impl<'a, 'b> Parser<'a, 'b>
.iter() .iter()
.filter(|s| { .filter(|s| {
starts(&s.p.meta.name[..], &*arg_os) || starts(&s.p.meta.name[..], &*arg_os) ||
(s.p (s.p.meta.aliases.is_some() &&
.meta
.aliases
.is_some() &&
s.p s.p
.meta .meta
.aliases .aliases
@ -673,7 +682,7 @@ impl<'a, 'b> Parser<'a, 'b>
if sc.meta.bin_name != self.meta.bin_name { if sc.meta.bin_name != self.meta.bin_name {
sc.meta.bin_name = Some(format!("{} {}", bin_name, sc.meta.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 // 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;"); debugln!("Parser::get_matches_with;");
// Verify all positional assertions pass // Verify all positional assertions pass
debug_assert!(self.verify_positionals()); debug_assert!(self.verify_positionals());
if self.positionals.values().any(|a| { if self.positionals
.values()
.any(|a| {
a.b.is_set(ArgSettings::Multiple) && a.b.is_set(ArgSettings::Multiple) &&
(a.index as usize != self.positionals.len()) (a.index as usize != self.positionals.len())
}) && }) &&
self.positionals.values().last().map_or(false, |p| !p.is_set(ArgSettings::Last)) { self.positionals
.values()
.last()
.map_or(false, |p| !p.is_set(ArgSettings::Last)) {
self.settings.set(AS::LowIndexMultiplePositional); self.settings.set(AS::LowIndexMultiplePositional);
} }
let has_args = self.has_args(); let has_args = self.has_args();
@ -836,7 +850,8 @@ impl<'a, 'b> Parser<'a, 'b>
!self.is_set(AS::InferSubcommands) { !self.is_set(AS::InferSubcommands) {
if let Some(cdate) = suggestions::did_you_mean(&*arg_os.to_string_lossy(), if let Some(cdate) = suggestions::did_you_mean(&*arg_os.to_string_lossy(),
sc_names!(self)) { 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(), .into_owned(),
cdate, cdate,
self.meta self.meta
@ -955,7 +970,8 @@ impl<'a, 'b> Parser<'a, 'b>
None), None),
self.color())); self.color()));
} else { } else {
return Err(Error::unrecognized_subcommand(arg_os.to_string_lossy() return Err(Error::unrecognized_subcommand(arg_os
.to_string_lossy()
.into_owned(), .into_owned(),
self.meta self.meta
.bin_name .bin_name
@ -977,10 +993,7 @@ impl<'a, 'b> Parser<'a, 'b>
}; };
try!(self.parse_subcommand(&*sc_name, matcher, it)); try!(self.parse_subcommand(&*sc_name, matcher, it));
} else if self.is_set(AS::SubcommandRequired) { } else if self.is_set(AS::SubcommandRequired) {
let bn = self.meta let bn = self.meta.bin_name.as_ref().unwrap_or(&self.meta.name);
.bin_name
.as_ref()
.unwrap_or(&self.meta.name);
return Err(Error::missing_subcommand(bn, return Err(Error::missing_subcommand(bn,
&usage::create_error_usage(self, matcher, None), &usage::create_error_usage(self, matcher, None),
self.color())); self.color()));
@ -1011,10 +1024,7 @@ impl<'a, 'b> Parser<'a, 'b>
debugln!("Parser::build_bin_names;"); debugln!("Parser::build_bin_names;");
for sc in &mut self.subcommands { for sc in &mut self.subcommands {
debug!("Parser::build_bin_names:iter: bin_name set..."); debug!("Parser::build_bin_names:iter: bin_name set...");
if sc.p if sc.p.meta.bin_name.is_none() {
.meta
.bin_name
.is_none() {
sdebugln!("No"); sdebugln!("No");
let bin_name = format!("{}{}{}", let bin_name = format!("{}{}{}",
self.meta self.meta
@ -1052,10 +1062,7 @@ impl<'a, 'b> Parser<'a, 'b>
debugln!("Parser::parse_subcommand;"); debugln!("Parser::parse_subcommand;");
let mut mid_string = String::new(); let mut mid_string = String::new();
if !self.is_set(AS::SubcommandsNegateReqs) { if !self.is_set(AS::SubcommandsNegateReqs) {
let mut hs: Vec<&str> = self.required let mut hs: Vec<&str> = self.required.iter().map(|n| &**n).collect();
.iter()
.map(|n| &**n)
.collect();
for k in matcher.arg_names() { for k in matcher.arg_names() {
hs.push(k); hs.push(k);
} }
@ -1066,26 +1073,23 @@ impl<'a, 'b> Parser<'a, 'b>
} }
} }
mid_string.push_str(" "); 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(); let mut sc_matcher = ArgMatcher::new();
// bin_name should be parent's bin_name + [<reqs>] + the sc's name separated by // bin_name should be parent's bin_name + [<reqs>] + the sc's name separated by
// a space // a space
sc.p.meta.usage = Some(format!("{}{}{}", sc.p.meta.usage = Some(format!("{}{}{}",
self.meta self.meta.bin_name.as_ref().unwrap_or(&String::new()),
.bin_name
.as_ref()
.unwrap_or(&String::new()),
if self.meta.bin_name.is_some() { if self.meta.bin_name.is_some() {
&*mid_string &*mid_string
} else { } else {
"" ""
}, },
&*sc.p.meta.name)); &*sc.p.meta.name));
sc.p.meta.bin_name = Some(format!("{}{}{}", sc.p.meta.bin_name =
self.meta Some(format!("{}{}{}",
.bin_name self.meta.bin_name.as_ref().unwrap_or(&String::new()),
.as_ref()
.unwrap_or(&String::new()),
if self.meta.bin_name.is_some() { if self.meta.bin_name.is_some() {
" " " "
} else { } else {
@ -1097,10 +1101,7 @@ impl<'a, 'b> Parser<'a, 'b>
debugln!("Parser::parse_subcommand: sc settings={:#?}", sc.p.settings); debugln!("Parser::parse_subcommand: sc settings={:#?}", sc.p.settings);
try!(sc.p.get_matches_with(&mut sc_matcher, it)); try!(sc.p.get_matches_with(&mut sc_matcher, it));
matcher.subcommand(SubCommand { matcher.subcommand(SubCommand {
name: sc.p name: sc.p.meta.name.clone(),
.meta
.name
.clone(),
matches: sc_matcher.into(), matches: sc_matcher.into(),
}); });
} }
@ -1211,7 +1212,8 @@ impl<'a, 'b> Parser<'a, 'b>
let arg = FlagBuilder { let arg = FlagBuilder {
b: Base { b: Base {
name: "vclap_version", name: "vclap_version",
help: self.version_message.or(Some("Prints version information")), help: self.version_message
.or(Some("Prints version information")),
..Default::default() ..Default::default()
}, },
s: Switched { s: Switched {
@ -1239,11 +1241,11 @@ impl<'a, 'b> Parser<'a, 'b>
arg.to_str().unwrap()); arg.to_str().unwrap());
if arg == "help" && self.is_set(AS::NeedsLongHelp) { if arg == "help" && self.is_set(AS::NeedsLongHelp) {
sdebugln!("Help"); sdebugln!("Help");
try!(self._help()); try!(self._help(true));
} }
if arg == "version" && self.is_set(AS::NeedsLongVersion) { if arg == "version" && self.is_set(AS::NeedsLongVersion) {
sdebugln!("Version"); sdebugln!("Version");
try!(self._version()); try!(self._version(true));
} }
sdebugln!("Neither"); sdebugln!("Neither");
@ -1257,22 +1259,37 @@ impl<'a, 'b> Parser<'a, 'b>
if let Some(h) = self.help_short { if let Some(h) = self.help_short {
if arg == h && self.is_set(AS::NeedsLongHelp) { if arg == h && self.is_set(AS::NeedsLongHelp) {
sdebugln!("Help"); sdebugln!("Help");
try!(self._help()); try!(self._help(false));
} }
} }
if let Some(v) = self.version_short { if let Some(v) = self.version_short {
if arg == v && self.is_set(AS::NeedsLongVersion) { if arg == v && self.is_set(AS::NeedsLongVersion) {
sdebugln!("Version"); sdebugln!("Version");
try!(self._version()); try!(self._version(false));
} }
} }
sdebugln!("Neither"); sdebugln!("Neither");
Ok(()) 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![]; let mut buf = vec![];
try!(Help::write_parser_help(&mut buf, self)); try!(Help::write_parser_help(&mut buf, self, use_long));
Err(Error { Err(Error {
message: unsafe { String::from_utf8_unchecked(buf) }, message: unsafe { String::from_utf8_unchecked(buf) },
kind: ErrorKind::HelpDisplayed, 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 out = io::stdout();
let mut buf_w = BufWriter::new(out.lock()); 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 { Err(Error {
message: String::new(), message: String::new(),
kind: ErrorKind::VersionDisplayed, kind: ErrorKind::VersionDisplayed,
@ -1344,7 +1362,8 @@ impl<'a, 'b> Parser<'a, 'b>
} }
debugln!("Parser::parse_long_arg: Didn't match anything"); 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))] #[cfg_attr(feature = "lints", allow(len_zero))]
@ -1470,7 +1489,8 @@ impl<'a, 'b> Parser<'a, 'b>
matcher.inc_occurrence_of(opt.b.name); matcher.inc_occurrence_of(opt.b.name);
// Increment or create the group "args" // 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() || if val.is_none() ||
!has_eq && !has_eq &&
@ -1556,7 +1576,8 @@ impl<'a, 'b> Parser<'a, 'b>
matcher.inc_occurrence_of(flag.b.name); matcher.inc_occurrence_of(flag.b.name);
// Increment or create the group "args" // 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(()) Ok(())
} }
@ -1589,30 +1610,30 @@ impl<'a, 'b> Parser<'a, 'b>
} }
// Prints the version to the user and exits if quit=true // Prints the version to the user and exits if quit=true
fn print_version<W: Write>(&self, w: &mut W) -> ClapResult<()> { fn print_version<W: Write>(&self, w: &mut W, use_long: bool) -> ClapResult<()> {
try!(self.write_version(w)); try!(self.write_version(w, use_long));
w.flush().map_err(Error::from) w.flush().map_err(Error::from)
} }
pub fn write_version<W: Write>(&self, w: &mut W) -> io::Result<()> { pub fn write_version<W: Write>(&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 let Some(bn) = self.meta.bin_name.as_ref() {
if bn.contains(' ') { if bn.contains(' ') {
// Incase we're dealing with subcommands i.e. git mv is translated to git-mv // Incase we're dealing with subcommands i.e. git mv is translated to git-mv
write!(w, write!(w, "{} {}", bn.replace(" ", "-"), ver)
"{} {}",
bn.replace(" ", "-"),
self.meta.version.unwrap_or("".into()))
} else { } else {
write!(w, write!(w, "{} {}", &self.meta.name[..], ver)
"{} {}",
&self.meta.name[..],
self.meta.version.unwrap_or("".into()))
} }
} else { } else {
write!(w, write!(w, "{} {}", &self.meta.name[..], ver)
"{} {}",
&self.meta.name[..],
self.meta.version.unwrap_or("".into()))
} }
} }
@ -1623,7 +1644,11 @@ impl<'a, 'b> Parser<'a, 'b>
} }
pub fn write_help<W: Write>(&self, w: &mut W) -> ClapResult<()> { pub fn write_help<W: Write>(&self, w: &mut W) -> ClapResult<()> {
Help::write_parser_help(w, self) Help::write_parser_help(w, self, false)
}
pub fn write_long_help<W: Write>(&self, w: &mut W) -> ClapResult<()> {
Help::write_parser_help(w, self, true)
} }
pub fn write_help_err<W: Write>(&self, w: &mut W) -> ClapResult<()> { pub fn write_help_err<W: Write>(&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>> { pub fn find_subcommand(&'b self, sc: &str) -> Option<&'b App<'a, 'b>> {
debugln!("Parser::find_subcommand: sc={}", sc); debugln!("Parser::find_subcommand: sc={}", sc);
debugln!("Parser::find_subcommand: Currently in Parser...{}", debugln!("Parser::find_subcommand: Currently in Parser...{}",
self.meta self.meta.bin_name.as_ref().unwrap());
.bin_name
.as_ref()
.unwrap());
for s in self.subcommands.iter() { for s in self.subcommands.iter() {
if s.p if s.p.meta.bin_name.as_ref().unwrap_or(&String::new()) == sc ||
.meta (s.p.meta.aliases.is_some() &&
.bin_name
.as_ref()
.unwrap_or(&String::new()) == sc ||
(s.p
.meta
.aliases
.is_some() &&
s.p s.p
.meta .meta
.aliases .aliases
@ -1751,11 +1766,7 @@ impl<'a, 'b> Parser<'a, 'b>
.unwrap() .unwrap()
.iter() .iter()
.any(|&(s, _)| { .any(|&(s, _)| {
s == s == sc.split(' ').rev().next().expect(INTERNAL_ERROR_MSG)
sc.split(' ')
.rev()
.next()
.expect(INTERNAL_ERROR_MSG)
})) { })) {
return Some(s); return Some(s);
} }

View file

@ -32,6 +32,7 @@ pub trait AnyArg<'n, 'e>: std_fmt::Display {
fn takes_value(&self) -> bool; fn takes_value(&self) -> bool;
fn val_names(&self) -> Option<&VecMap<&'e str>>; fn val_names(&self) -> Option<&VecMap<&'e str>>;
fn help(&self) -> Option<&'e str>; fn help(&self) -> Option<&'e str>;
fn long_help(&self) -> Option<&'e str>;
fn default_val(&self) -> Option<&'e OsStr>; fn default_val(&self) -> Option<&'e OsStr>;
fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>>; fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>>;
fn longest_filter(&self) -> bool; fn longest_filter(&self) -> bool;

View file

@ -103,6 +103,7 @@ impl<'a, 'b> Arg<'a, 'b> {
"long" => yaml_to_str!(a, v, long), "long" => yaml_to_str!(a, v, long),
"aliases" => yaml_vec_or_str!(v, a, alias), "aliases" => yaml_vec_or_str!(v, a, alias),
"help" => yaml_to_str!(a, v, help), "help" => yaml_to_str!(a, v, help),
"long_help" => yaml_to_str!(a, v, long_help),
"required" => yaml_to_bool!(a, v, required), "required" => yaml_to_bool!(a, v, required),
"required_if" => yaml_tuple2!(a, v, required_if), "required_if" => yaml_tuple2!(a, v, required_if),
"required_ifs" => 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 self
} }
/// Sets the help text of the argument that will be displayed to the user when they print the /// Sets the short help text of the argument that will be displayed to the user when they print
/// usage/help information. /// 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 /// # Examples
/// ///
@ -532,11 +539,83 @@ impl<'a, 'b> Arg<'a, 'b> {
/// -h, --help Prints help information /// -h, --help Prints help information
/// -V, --version Prints version information /// -V, --version Prints version information
/// ``` /// ```
/// [`Arg::long_help`]: ./struct.Arg.html#method.long_help
pub fn help(mut self, h: &'b str) -> Self { pub fn help(mut self, h: &'b str) -> Self {
self.b.help = Some(h); self.b.help = Some(h);
self 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 /// 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 -- /// 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 /// last_arg`). Even, if no other arguments are left to parse, if the user omits the `--` syntax

View file

@ -7,6 +7,7 @@ pub struct Base<'a, 'b>
{ {
pub name: &'a str, pub name: &'a str,
pub help: Option<&'b str>, pub help: Option<&'b str>,
pub long_help: Option<&'b str>,
pub blacklist: Option<Vec<&'a str>>, pub blacklist: Option<Vec<&'a str>>,
pub settings: ArgFlags, pub settings: ArgFlags,
pub r_unless: Option<Vec<&'a str>>, pub r_unless: Option<Vec<&'a str>>,

View file

@ -79,6 +79,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
fn long(&self) -> Option<&'e str> { self.s.long } fn long(&self) -> Option<&'e str> { self.s.long }
fn val_delim(&self) -> Option<char> { None } fn val_delim(&self) -> Option<char> { None }
fn help(&self) -> Option<&'e str> { self.b.help } 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 val_terminator(&self) -> Option<&'e str> { None }
fn default_val(&self) -> Option<&'e OsStr> { None } fn default_val(&self) -> Option<&'e OsStr> { None }
fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> { fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {

View file

@ -129,6 +129,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
fn val_delim(&self) -> Option<char> { self.v.val_delim } fn val_delim(&self) -> Option<char> { self.v.val_delim }
fn takes_value(&self) -> bool { true } fn takes_value(&self) -> bool { true }
fn help(&self) -> Option<&'e str> { self.b.help } 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_val(&self) -> Option<&'e OsStr> { self.v.default_val }
fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> { fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
self.v.default_vals_ifs.as_ref().map(|vm| vm.values()) self.v.default_vals_ifs.as_ref().map(|vm| vm.values())

View file

@ -126,6 +126,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
fn val_delim(&self) -> Option<char> { self.v.val_delim } fn val_delim(&self) -> Option<char> { self.v.val_delim }
fn takes_value(&self) -> bool { true } fn takes_value(&self) -> bool { true }
fn help(&self) -> Option<&'e str> { self.b.help } 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<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> { fn default_vals_ifs(&self) -> Option<vec_map::Values<(&'n str, Option<&'e OsStr>, &'e OsStr)>> {
self.v.default_vals_ifs.as_ref().map(|vm| vm.values()) self.v.default_vals_ifs.as_ref().map(|vm| vm.values())
} }

View file

@ -562,6 +562,8 @@ macro_rules! app_from_crate {
/// ///
/// * A single hyphen followed by a character (such as `-c`) sets the [`Arg::short`] /// * 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`] /// * 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)`] /// * Three dots (`...`) sets [`Arg::multiple(true)`]
/// * Angled brackets after either a short or long will set [`Arg::value_name`] and /// * Angled brackets after either a short or long will set [`Arg::value_name`] and
/// `Arg::required(true)` such as `--config <FILE>` = `Arg::value_name("FILE")` and /// `Arg::required(true)` such as `--config <FILE>` = `Arg::value_name("FILE")` and
@ -595,6 +597,13 @@ macro_rules! app_from_crate {
#[macro_export] #[macro_export]
macro_rules! clap_app { macro_rules! clap_app {
(@app ($builder:expr)) => { $builder }; (@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)*) => { (@app ($builder:expr) (@arg $name:ident: $($tail:tt)*) $($tt:tt)*) => {
clap_app!{ @app clap_app!{ @app
($builder.arg( ($builder.arg(

View file

@ -831,3 +831,16 @@ fn hidden_default_val() {
.default_value("default-argument")); .default_value("default-argument"));
assert!(test::compare_output(app2, "default --help", HIDE_DEFAULT_VAL, false)); 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"));
}

View file

@ -110,3 +110,40 @@ fn quoted_arg_long_name() {
.expect("Expected to successfully match the given args."); .expect("Expected to successfully match the given args.");
assert!(matches.is_present("option2")); 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. <kbknapp@gmail.com>")
(@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. <kbknapp@gmail.com>")
(@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"));
}

View file

@ -3,7 +3,7 @@ extern crate regex;
use std::str; use std::str;
use clap::{App, ErrorKind}; use clap::{App, Arg, ErrorKind};
include!("../clap-test.rs"); include!("../clap-test.rs");
@ -43,3 +43,16 @@ fn complex_version_output() {
a.write_version(&mut ver).unwrap(); a.write_version(&mut ver).unwrap();
assert_eq!(str::from_utf8(&ver).unwrap(), VERSION); 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"));
}