Auto merge of #449 - kbknapp:issue-428-redux, r=kbknapp

Issue 428 redux
This commit is contained in:
Homu 2016-03-15 20:48:13 +09:00
commit f6d1685000
20 changed files with 486 additions and 211 deletions

View file

@ -1,3 +1,24 @@
<a name="v2.2.0"></a>
## v2.2.0 (2016-03-14)
#### Documentation
* **Groups:** explains required ArgGroups better ([4ff0205b](https://github.com/kbknapp/clap-rs/commit/4ff0205b85a45151b59bbaf090a89df13438380f), closes [#439](https://github.com/kbknapp/clap-rs/issues/439))
#### Features
* **Help Message:** can auto wrap and aligning help text to term width ([e36af026](https://github.com/kbknapp/clap-rs/commit/e36af0266635f23e85e951b9088d561e9a5d1bf6), closes [#428](https://github.com/kbknapp/clap-rs/issues/428))
* **Opts and Flags:** adds support for custom ordering in help messages ([9803b51e](https://github.com/kbknapp/clap-rs/commit/9803b51e799904c0befaac457418ee766ccc1ab9))
* **Settings:** adds support for automatically deriving custom display order of args ([ad86e433](https://github.com/kbknapp/clap-rs/commit/ad86e43334c4f70e86909689a088fb87e26ff95a), closes [#444](https://github.com/kbknapp/clap-rs/issues/444))
* **Subcommands:** adds support for custom ordering in help messages ([7d2a2ed4](https://github.com/kbknapp/clap-rs/commit/7d2a2ed413f5517d45988eef0765cdcd663b6372), closes [#442](https://github.com/kbknapp/clap-rs/issues/442))
#### Bug Fixes
* **From Usage:** fixes a bug where adding empty lines werent ignored ([c5c58c86](https://github.com/kbknapp/clap-rs/commit/c5c58c86b9c503d8de19da356a5a5cffb59fbe84))
<a name="v2.1.2"></a> <a name="v2.1.2"></a>
### v2.1.2 (2016-02-24) ### v2.1.2 (2016-02-24)

View file

@ -1,7 +1,7 @@
[package] [package]
name = "clap" name = "clap"
version = "2.1.2" version = "2.2.0"
authors = ["Kevin K. <kbknapp@gmail.com>"] authors = ["Kevin K. <kbknapp@gmail.com>"]
exclude = ["examples/*", "clap-tests/*", "tests/*", "benches/*", "*.png", "clap-perf/*"] exclude = ["examples/*", "clap-tests/*", "tests/*", "benches/*", "*.png", "clap-perf/*"]
description = "A simple to use, efficient, and full featured Command Line Argument Parser" description = "A simple to use, efficient, and full featured Command Line Argument Parser"
@ -14,16 +14,18 @@ keywords = ["argument", "command", "arg", "parser", "parse"]
[dependencies] [dependencies]
bitflags = "~0.4" bitflags = "~0.4"
vec_map = "~0.6" vec_map = "~0.6"
libc = { version = "~0.2.8", optional = true }
ansi_term = { version = "~0.7.2", optional = true } ansi_term = { version = "~0.7.2", optional = true }
strsim = { version = "~0.4.0", optional = true } strsim = { version = "~0.4.0", optional = true }
yaml-rust = { version = "~0.3", optional = true } yaml-rust = { version = "~0.3", optional = true }
clippy = { version = "~0.0.48", optional = true } clippy = { version = "~0.0.48", optional = true }
[features] [features]
default = ["suggestions", "color"] default = ["suggestions", "color", "wrap_help"]
suggestions = ["strsim"] suggestions = ["strsim"]
color = ["ansi_term"] color = ["ansi_term"]
yaml = ["yaml-rust"] yaml = ["yaml-rust"]
wrap_help = ["libc"]
lints = ["clippy", "nightly"] lints = ["clippy", "nightly"]
nightly = [] # for building with nightly and unstable features nightly = [] # for building with nightly and unstable features
unstable = [] # for building with unstable features on stable Rust unstable = [] # for building with unstable features on stable Rust

View file

@ -38,7 +38,20 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
## What's New ## What's New
In v2.1.1 Here's the highlights from v2.2.0
#### Features
* **Help text auto wraps and aligns at term width!** - Long help strings will now properly wrap and align to term width on Linux and OSX (and resumably Unix too). This can be turned off as well.
* **Can customize the order of opts, flags, and subcommands in help messages** - Instead of using the default alphabetical order, you can now re-arange the order of your args and subcommands in help message. This helps to emphasize more popular or important options.
* **Can auto-derive the order from declaration order** - Have a bunch of args or subcommmands to re-order? You can now just derive the order from the declaration order!
* Other minor bug fixes
An example of the help text wrapping at term width:
![screenshot](http://i.imgur.com/PAJzJJG.png)
In v2.1.2
#### New Features #### New Features
@ -47,7 +60,7 @@ In v2.1.1
#### Improvements #### Improvements
* **Documenation Examples**: The examples in the documentation have been vastly improved * **Documentation Examples**: The examples in the documentation have been vastly improved
For full details, see [CHANGELOG.md](https://github.com/kbknapp/clap-rs/blob/master/CHANGELOG.md) For full details, see [CHANGELOG.md](https://github.com/kbknapp/clap-rs/blob/master/CHANGELOG.md)
@ -433,14 +446,14 @@ default-features = false
# Cherry-pick the features you'd like to use # Cherry-pick the features you'd like to use
features = [ "suggestions", "color" ] features = [ "suggestions", "color" ]
``` ```
The following is a list of optional `clap` features: The following is a list of optional `clap` features:
* **"suggestions"**: Turns on the `Did you mean '--myoption' ?` feature for when users make typos. * **"suggestions"**: Turns on the `Did you mean '--myoption' ?` feature for when users make typos. (builds dependency `strsim`)
* **"color"**: Turns on colored error messages. This feature only works on non-Windows OSs. * **"color"**: Turns on colored error messages. This feature only works on non-Windows OSs. (builds dependency `ansi-term`)
* **"lints"**: This is **not** included by default and should only be used while developing to run basic lints against changes. This can only be used on Rust nightly. * **"wrap_help"**: Automatically detects terminal width and wraps long help text lines with proper indentation alignment (builds dependency `libc`)
* **"lints"**: This is **not** included by default and should only be used while developing to run basic lints against changes. This can only be used on Rust nightly. (builds dependency `clippy`)
* **"debug"**: This is **not** included by default and should only be used while developing to display debugging information. * **"debug"**: This is **not** included by default and should only be used while developing to display debugging information.
* **"yaml"**: This is **not** included by default. Enables building CLIs from YAML documents. * **"yaml"**: This is **not** included by default. Enables building CLIs from YAML documents. (builds dependency `yaml-rust`)
* **"unstable"**: This is **not** included by default. Enables unstable features, unstable refers to whether or not they may change, not performance stability. * **"unstable"**: This is **not** included by default. Enables unstable features, unstable refers to whether or not they may change, not performance stability.
### Dependencies Tree ### Dependencies Tree
@ -472,7 +485,7 @@ Contributions are always welcome! And there is a multitude of ways in which you
Another really great way to help is if you find an interesting, or helpful way in which to use `clap`. You can either add it to the [examples/](examples) directory, or file an issue and tell me. I'm all about giving credit where credit is due :) Another really great way to help is if you find an interesting, or helpful way in which to use `clap`. You can either add it to the [examples/](examples) directory, or file an issue and tell me. I'm all about giving credit where credit is due :)
Please read [CONTRIBUTING.md](CONTRIBUTING.md) before you start contributing. Please read [CONTRIBUTING.md](.github/CONTRIBUTING.md) before you start contributing.
### Running the tests ### Running the tests
@ -553,6 +566,6 @@ As of 2.0.0 (From 1.x)
Old method names will be left around for several minor version bumps, or one major version bump. Old method names will be left around for several minor version bumps, or one major version bump.
As of 2.1.1: As of 2.2.0:
* None! * None!

View file

@ -13,7 +13,7 @@ Kevin K. <kbknapp@gmail.com>
tests clap library tests clap library
USAGE: USAGE:
\tclaptests [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND] claptests [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND]
FLAGS: FLAGS:
-f, --flag tests flags -f, --flag tests flags
@ -47,7 +47,7 @@ _sc_dym_usage = '''error: The subcommand 'subcm' wasn't recognized
If you believe you received this message in error, try re-running with 'claptests -- subcm' If you believe you received this message in error, try re-running with 'claptests -- subcm'
USAGE: USAGE:
\tclaptests [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND] claptests [FLAGS] [OPTIONS] [ARGS] [SUBCOMMAND]
For more information try --help''' For more information try --help'''
@ -55,7 +55,7 @@ _arg_dym_usage = '''error: Found argument '--optio' which wasn't expected, or is
\tDid you mean --option ? \tDid you mean --option ?
USAGE: USAGE:
\tclaptests --option <opt>... claptests --option <opt>...
For more information try --help''' For more information try --help'''
@ -65,30 +65,30 @@ _pv_dym_usage = '''error: 'slo' isn't a valid value for '--Option <option3>'
Did you mean 'slow' ? Did you mean 'slow' ?
USAGE: USAGE:
\tclaptests --Option <option3> claptests --Option <option3>
For more information try --help''' For more information try --help'''
_excluded = '''error: The argument '--flag' cannot be used with '-F' _excluded = '''error: The argument '--flag' cannot be used with '-F'
USAGE: USAGE:
\tclaptests [positional2] -F --long-option-2 <option2> claptests [positional2] -F --long-option-2 <option2>
For more information try --help''' For more information try --help'''
_excluded_l = '''error: The argument '-f' cannot be used with '-F' _excluded_l = '''error: The argument '-f' cannot be used with '-F'
USAGE: USAGE:
\tclaptests [positional2] -F --long-option-2 <option2> claptests [positional2] -F --long-option-2 <option2>
For more information try --help''' For more information try --help'''
_required = '''error: The following required arguments were not provided: _required = '''error: The following required arguments were not provided:
\t[positional2] [positional2]
\t--long-option-2 <option2> --long-option-2 <option2>
USAGE: USAGE:
\tclaptests [positional2] -F --long-option-2 <option2> claptests [positional2] -F --long-option-2 <option2>
For more information try --help''' For more information try --help'''
@ -141,7 +141,7 @@ Kevin K. <kbknapp@gmail.com>
tests subcommands tests subcommands
USAGE: USAGE:
\tclaptests subcmd [FLAGS] [OPTIONS] [--] [ARGS] claptests subcmd [FLAGS] [OPTIONS] [--] [ARGS]
FLAGS: FLAGS:
-f, --flag tests flags -f, --flag tests flags

Binary file not shown.

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View file

@ -1235,7 +1235,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
&*self.get_required_from(&*self.required.iter().map(|&r| &*r).collect::<Vec<_>>(), Some(matcher)) &*self.get_required_from(&*self.required.iter().map(|&r| &*r).collect::<Vec<_>>(), Some(matcher))
.iter() .iter()
.fold(String::new(), .fold(String::new(),
|acc, s| acc + &format!("\n\t{}", Format::Error(s))[..]), |acc, s| acc + &format!("\n {}", Format::Error(s))[..]),
&*self.create_current_usage(matcher)) &*self.create_current_usage(matcher))
}; };
return Err(err); return Err(err);
@ -1290,7 +1290,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
fn create_usage(&self, used: &[&str]) -> String { fn create_usage(&self, used: &[&str]) -> String {
debugln!("fn=create_usage;"); debugln!("fn=create_usage;");
let mut usage = String::with_capacity(75); let mut usage = String::with_capacity(75);
usage.push_str("USAGE:\n\t"); usage.push_str("USAGE:\n ");
if let Some(u) = self.meta.usage_str { if let Some(u) = self.meta.usage_str {
usage.push_str(&*u); usage.push_str(&*u);
} else if used.is_empty() { } else if used.is_empty() {
@ -1448,7 +1448,6 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
try!(write!(w, "\n")); try!(write!(w, "\n"));
} }
let tab = " ";
let longest = if !unified_help || longest_opt == 0 { let longest = if !unified_help || longest_opt == 0 {
longest_flag longest_flag
} else { } else {
@ -1461,13 +1460,13 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
for f in self.flags.iter().filter(|f| !f.settings.is_set(ArgSettings::Hidden)) { for f in self.flags.iter().filter(|f| !f.settings.is_set(ArgSettings::Hidden)) {
let btm = ord_m.entry(f.disp_ord).or_insert(BTreeMap::new()); let btm = ord_m.entry(f.disp_ord).or_insert(BTreeMap::new());
let mut v = vec![]; let mut v = vec![];
try!(f.write_help(&mut v, tab, longest, nlh)); try!(f.write_help(&mut v, longest, nlh));
btm.insert(f.name, v); btm.insert(f.name, v);
} }
for o in self.opts.iter().filter(|o| !o.settings.is_set(ArgSettings::Hidden)) { for o in self.opts.iter().filter(|o| !o.settings.is_set(ArgSettings::Hidden)) {
let btm = ord_m.entry(o.disp_ord).or_insert(BTreeMap::new()); let btm = ord_m.entry(o.disp_ord).or_insert(BTreeMap::new());
let mut v = vec![]; let mut v = vec![];
try!(o.write_help(&mut v, tab, longest, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh)); try!(o.write_help(&mut v, longest, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh));
btm.insert(o.name, v); btm.insert(o.name, v);
} }
for (_, btm) in ord_m.into_iter() { for (_, btm) in ord_m.into_iter() {
@ -1486,7 +1485,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
} }
for (_, btm) in ord_m.into_iter() { for (_, btm) in ord_m.into_iter() {
for (_, f) in btm.into_iter() { for (_, f) in btm.into_iter() {
try!(f.write_help(w, tab, longest, nlh)); try!(f.write_help(w, longest, nlh));
} }
} }
} }
@ -1499,7 +1498,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
} }
for (_, btm) in ord_m.into_iter() { for (_, btm) in ord_m.into_iter() {
for (_, o) in btm.into_iter() { for (_, o) in btm.into_iter() {
try!(o.write_help(w, tab, longest_opt, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh)); try!(o.write_help(w, longest_opt, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh));
} }
} }
} }
@ -1508,7 +1507,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
try!(write!(w, "\nARGS:\n")); try!(write!(w, "\nARGS:\n"));
for v in self.positionals.values() for v in self.positionals.values()
.filter(|p| !p.settings.is_set(ArgSettings::Hidden)) { .filter(|p| !p.settings.is_set(ArgSettings::Hidden)) {
try!(v.write_help(w, tab, longest_pos, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh)); try!(v.write_help(w, longest_pos, self.is_set(AppSettings::HidePossibleValuesInHelp), nlh));
} }
} }
if subcmds { if subcmds {
@ -1520,7 +1519,7 @@ impl<'a, 'b> Parser<'a, 'b> where 'a: 'b {
} }
for (_, btm) in ord_m.into_iter() { for (_, btm) in ord_m.into_iter() {
for (name, sc) in btm.into_iter() { for (name, sc) in btm.into_iter() {
try!(write!(w, "{}{}", tab, name)); try!(write!(w, " {}", name));
write_spaces!((longest_sc + 4) - (name.len()), w); write_spaces!((longest_sc + 4) - (name.len()), w);
if let Some(a) = sc.p.meta.about { if let Some(a) = sc.p.meta.about {
if a.contains("{n}") { if a.contains("{n}") {

View file

@ -476,3 +476,29 @@ impl FromStr for AppSettings {
} }
} }
} }
#[cfg(test)]
mod test {
use super::AppSettings;
#[test]
fn app_settings_fromstr() {
assert_eq!("subcommandsnegatereqs".parse::<AppSettings>().unwrap(), AppSettings::SubcommandsNegateReqs);
assert_eq!("subcommandsrequired".parse::<AppSettings>().unwrap(), AppSettings::SubcommandRequired);
assert_eq!("argrequiredelsehelp".parse::<AppSettings>().unwrap(), AppSettings::ArgRequiredElseHelp);
assert_eq!("globalversion".parse::<AppSettings>().unwrap(), AppSettings::GlobalVersion);
assert_eq!("versionlesssubcommands".parse::<AppSettings>().unwrap(), AppSettings::VersionlessSubcommands);
assert_eq!("unifiedhelpmessage".parse::<AppSettings>().unwrap(), AppSettings::UnifiedHelpMessage);
assert_eq!("waitonerror".parse::<AppSettings>().unwrap(), AppSettings::WaitOnError);
assert_eq!("subcommandrequiredelsehelp".parse::<AppSettings>().unwrap(), AppSettings::SubcommandRequiredElseHelp);
assert_eq!("allowexternalsubcommands".parse::<AppSettings>().unwrap(), AppSettings::AllowExternalSubcommands);
assert_eq!("trailingvararg".parse::<AppSettings>().unwrap(), AppSettings::TrailingVarArg);
assert_eq!("nobinaryname".parse::<AppSettings>().unwrap(), AppSettings::NoBinaryName);
assert_eq!("strictutf8".parse::<AppSettings>().unwrap(), AppSettings::StrictUtf8);
assert_eq!("allowinvalidutf8".parse::<AppSettings>().unwrap(), AppSettings::AllowInvalidUtf8);
assert_eq!("allowleadinghyphen".parse::<AppSettings>().unwrap(), AppSettings::AllowLeadingHyphen);
assert_eq!("hidepossiblevaluesinhelp".parse::<AppSettings>().unwrap(), AppSettings::HidePossibleValuesInHelp);
assert_eq!("hidden".parse::<AppSettings>().unwrap(), AppSettings::Hidden);
assert!("hahahaha".parse::<AppSettings>().is_err());
}
}

View file

@ -1,6 +1,8 @@
use std::rc::Rc; use std::rc::Rc;
use std::fmt::Display; use std::fmt::Display;
use vec_map::VecMap;
use args::settings::ArgSettings; use args::settings::ArgSettings;
#[doc(hidden)] #[doc(hidden)]
@ -20,4 +22,8 @@ pub trait AnyArg<'n, 'e>: Display {
fn short(&self) -> Option<char>; fn short(&self) -> Option<char>;
fn long(&self) -> Option<&'e str>; fn long(&self) -> Option<&'e str>;
fn val_delim(&self) -> Option<char>; fn val_delim(&self) -> Option<char>;
fn takes_value(&self) -> bool;
fn val_names(&self) -> Option<&VecMap<&'e str>>;
fn help(&self) -> Option<&'e str>;
fn default_val(&self) -> Option<&'n str>;
} }

View file

@ -5,8 +5,10 @@ use std::io;
use std::rc::Rc; use std::rc::Rc;
use std::result::Result as StdResult; use std::result::Result as StdResult;
use vec_map::VecMap;
use Arg; use Arg;
use args::AnyArg; use args::{AnyArg, HelpWriter};
use args::settings::{ArgFlags, ArgSettings}; use args::settings::{ArgFlags, ArgSettings};
#[derive(Debug)] #[derive(Debug)]
@ -47,9 +49,9 @@ impl<'n, 'e> FlagBuilder<'n, 'e> {
} }
} }
pub fn write_help<W: io::Write>(&self, w: &mut W, tab: &str, longest: usize, nlh: bool) -> io::Result<()> { pub fn write_help<W: io::Write>(&self, w: &mut W, longest: usize, nlh: bool) -> io::Result<()> {
write_arg_help!(@flag self, w, tab, longest, nlh); let hw = HelpWriter::new(self, longest, nlh);
write!(w, "\n") hw.write_to(w)
} }
} }
@ -98,8 +100,10 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
fn blacklist(&self) -> Option<&[&'e str]> { self.blacklist.as_ref().map(|o| &o[..]) } fn blacklist(&self) -> Option<&[&'e str]> { self.blacklist.as_ref().map(|o| &o[..]) }
fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) } fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) }
fn has_switch(&self) -> bool { true } fn has_switch(&self) -> bool { true }
fn takes_value(&self) -> bool { false }
fn set(&mut self, s: ArgSettings) { self.settings.set(s) } fn set(&mut self, s: ArgSettings) { self.settings.set(s) }
fn max_vals(&self) -> Option<u64> { None } fn max_vals(&self) -> Option<u64> { None }
fn val_names(&self) -> Option<&VecMap<&'e str>> { None }
fn num_vals(&self) -> Option<u64> { None } fn num_vals(&self) -> Option<u64> { None }
fn possible_vals(&self) -> Option<&[&'e str]> { None } fn possible_vals(&self) -> Option<&[&'e str]> { None }
fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None } fn validator(&self) -> Option<&Rc<Fn(String) -> StdResult<(), String>>> { None }
@ -107,6 +111,8 @@ impl<'n, 'e> AnyArg<'n, 'e> for FlagBuilder<'n, 'e> {
fn short(&self) -> Option<char> { self.short } fn short(&self) -> Option<char> { self.short }
fn long(&self) -> Option<&'e str> { self.long } fn long(&self) -> Option<&'e str> { self.long }
fn val_delim(&self) -> Option<char> { None } fn val_delim(&self) -> Option<char> { None }
fn help(&self) -> Option<&'e str> { self.help }
fn default_val(&self) -> Option<&'n str> { None }
} }
#[cfg(test)] #[cfg(test)]

View file

@ -1,126 +0,0 @@
macro_rules! write_arg_help {
(@opt $_self:ident, $w:ident, $tab:ident, $longest:ident, $skip_pv:ident, $nlh:ident) => {
write_arg_help!(@short $_self, $w, $tab);
write_arg_help!(@opt_long $_self, $w, $nlh, $longest);
write_arg_help!(@val $_self, $w);
if !($nlh || $_self.settings.is_set(ArgSettings::NextLineHelp)) {
write_spaces!(if $_self.long.is_some() { $longest + 4 } else { $longest + 8 } - ($_self.to_string().len()), $w);
}
if let Some(h) = $_self.help {
write_arg_help!(@help $_self, $w, h, $tab, $longest, $nlh);
write_arg_help!(@spec_vals $_self, $w, $skip_pv);
}
};
(@flag $_self:ident, $w:ident, $tab:ident, $longest:ident, $nlh:ident) => {
write_arg_help!(@short $_self, $w, $tab);
write_arg_help!(@flag_long $_self, $w, $longest, $nlh);
if let Some(h) = $_self.help {
write_arg_help!(@help $_self, $w, h, $tab, $longest, $nlh);
}
};
(@pos $_self:ident, $w:ident, $tab:ident, $longest:ident, $skip_pv:ident, $nlh:ident) => {
try!(write!($w, "{}", $tab));
write_arg_help!(@val $_self, $w);
if !($nlh || $_self.settings.is_set(ArgSettings::NextLineHelp)) {
write_spaces!($longest + 4 - ($_self.to_string().len()), $w);
}
if let Some(h) = $_self.help {
write_arg_help!(@help $_self, $w, h, $tab, $longest, $nlh);
write_arg_help!(@spec_vals $_self, $w, $skip_pv);
}
};
(@short $_self:ident, $w:ident, $tab:ident) => {
try!(write!($w, "{}", $tab));
if let Some(s) = $_self.short {
try!(write!($w, "-{}", s));
} else {
try!(write!($w, "{}", $tab));
}
};
(@flag_long $_self:ident, $w:ident, $longest:ident, $nlh:ident) => {
if let Some(l) = $_self.long {
write_arg_help!(@long $_self, $w, l);
if !$nlh || !$_self.settings.is_set(ArgSettings::NextLineHelp) {
write_spaces!(($longest + 4) - (l.len() + 2), $w);
}
} else {
if !$nlh || !$_self.settings.is_set(ArgSettings::NextLineHelp) {
// 6 is tab (4) + -- (2)
write_spaces!(($longest + 6), $w);
}
}
};
(@opt_long $_self:ident, $w:ident, $nlh:ident, $longest:ident) => {
if let Some(l) = $_self.long {
write_arg_help!(@long $_self, $w, l);
}
try!(write!($w, " "));
};
(@long $_self:ident, $w:ident, $l:ident) => {
try!(write!($w,
"{}--{}",
if $_self.short.is_some() {
", "
} else {
""
},
$l));
};
(@val $_self:ident, $w:ident) => {
if let Some(ref vec) = $_self.val_names {
let mut it = vec.iter().peekable();
while let Some((_, val)) = it.next() {
try!(write!($w, "<{}>", val));
if it.peek().is_some() { try!(write!($w, " ")); }
}
let num = vec.len();
if $_self.settings.is_set(ArgSettings::Multiple) && num == 1 {
try!(write!($w, "..."));
}
} else if let Some(num) = $_self.num_vals {
for _ in 0..num {
try!(write!($w, "<{}>", $_self.name));
}
} else {
try!(write!($w,
"<{}>{}",
$_self.name,
if $_self.settings.is_set(ArgSettings::Multiple) {
"..."
} else {
""
}));
}
};
(@spec_vals $_self:ident, $w:ident, $skip_pv:ident) => {
if let Some(ref pv) = $_self.default_val {
try!(write!($w, " [default: {}]", pv));
}
if !$skip_pv {
if let Some(ref pv) = $_self.possible_vals {
try!(write!($w, " [values: {}]", pv.join(", ")));
}
}
};
(@help $_self:ident, $w:ident, $h:ident, $tab:ident, $longest:expr, $nlh:ident) => {
if $nlh || $_self.settings.is_set(ArgSettings::NextLineHelp) {
try!(write!($w, "\n{}{}", $tab, $tab));
}
if $h.contains("{n}") {
if let Some(part) = $h.split("{n}").next() {
try!(write!($w, "{}", part));
}
for part in $h.split("{n}").skip(1) {
try!(write!($w, "\n"));
if $nlh || $_self.settings.is_set(ArgSettings::NextLineHelp) {
try!(write!($w, "{}{}", $tab, $tab));
} else {
write_spaces!($longest + 12, $w);
}
try!(write!($w, "{}", part));
}
} else {
try!(write!($w, "{}", $h));
}
};
}

View file

@ -2,8 +2,6 @@ pub use self::flag::FlagBuilder;
pub use self::option::OptBuilder; pub use self::option::OptBuilder;
pub use self::positional::PosBuilder; pub use self::positional::PosBuilder;
#[macro_use]
mod macros;
#[allow(dead_code)] #[allow(dead_code)]
mod flag; mod flag;
#[allow(dead_code)] #[allow(dead_code)]

View file

@ -5,7 +5,7 @@ use std::io;
use vec_map::VecMap; use vec_map::VecMap;
use args::{AnyArg, Arg}; use args::{AnyArg, Arg, HelpWriter};
use args::settings::{ArgFlags, ArgSettings}; use args::settings::{ArgFlags, ArgSettings};
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
@ -104,10 +104,10 @@ impl<'n, 'e> OptBuilder<'n, 'e> {
ob ob
} }
pub fn write_help<W: io::Write>(&self, w: &mut W, tab: &str, longest: usize, skip_pv: bool, nlh: bool) -> io::Result<()> { pub fn write_help<W: io::Write>(&self, w: &mut W, longest: usize, skip_pv: bool, nlh: bool) -> io::Result<()> {
debugln!("fn=write_help"); let mut hw = HelpWriter::new(self, longest, nlh);
write_arg_help!(@opt self, w, tab, longest, skip_pv, nlh); hw.skip_pv = skip_pv;
write!(w, "\n") hw.write_to(w)
} }
} }
@ -116,29 +116,30 @@ impl<'n, 'e> Display for OptBuilder<'n, 'e> {
debugln!("fn=fmt"); debugln!("fn=fmt");
// Write the name such --long or -l // Write the name such --long or -l
if let Some(l) = self.long { if let Some(l) = self.long {
try!(write!(f, "--{}", l)); try!(write!(f, "--{} ", l));
} else { } else {
try!(write!(f, "-{}", self.short.unwrap())); try!(write!(f, "-{} ", self.short.unwrap()));
} }
// Write the values such as <name1> <name2> // Write the values such as <name1> <name2>
if let Some(ref vec) = self.val_names { if let Some(ref vec) = self.val_names {
for (_, n) in vec { let mut it = vec.iter().peekable();
debugln!("writing val_name: {}", n); while let Some((_, val)) = it.next() {
try!(write!(f, " <{}>", n)); try!(write!(f, "<{}>", val));
if it.peek().is_some() { try!(write!(f, " ")); }
} }
let num = vec.len(); let num = vec.len();
if self.settings.is_set(ArgSettings::Multiple) && num == 1 { if self.is_set(ArgSettings::Multiple) && num == 1 {
try!(write!(f, "...")); try!(write!(f, "..."));
} }
} else if let Some(num) = self.num_vals {
let mut it = (0..num).peekable();
while let Some(_) = it.next() {
try!(write!(f, "<{}>", self.name));
if it.peek().is_some() { try!(write!(f, " ")); }
}
} else { } else {
let num = self.num_vals.unwrap_or(1); try!(write!(f, "<{}>{}", self.name, if self.is_set(ArgSettings::Multiple) { "..." } else { "" }));
for _ in 0..num {
try!(write!(f, " <{}>", self.name));
}
if self.settings.is_set(ArgSettings::Multiple) && num == 1 {
try!(write!(f, "..."));
}
} }
Ok(()) Ok(())
@ -150,6 +151,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
fn overrides(&self) -> Option<&[&'e str]> { self.overrides.as_ref().map(|o| &o[..]) } fn overrides(&self) -> Option<&[&'e str]> { self.overrides.as_ref().map(|o| &o[..]) }
fn requires(&self) -> Option<&[&'e str]> { self.requires.as_ref().map(|o| &o[..]) } fn requires(&self) -> Option<&[&'e str]> { self.requires.as_ref().map(|o| &o[..]) }
fn blacklist(&self) -> Option<&[&'e str]> { self.blacklist.as_ref().map(|o| &o[..]) } fn blacklist(&self) -> Option<&[&'e str]> { self.blacklist.as_ref().map(|o| &o[..]) }
fn val_names(&self) -> Option<&VecMap<&'e str>> { self.val_names.as_ref().map(|o| o) }
fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) } fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) }
fn has_switch(&self) -> bool { true } fn has_switch(&self) -> bool { true }
fn set(&mut self, s: ArgSettings) { self.settings.set(s) } fn set(&mut self, s: ArgSettings) { self.settings.set(s) }
@ -163,6 +165,9 @@ impl<'n, 'e> AnyArg<'n, 'e> for OptBuilder<'n, 'e> {
fn short(&self) -> Option<char> { self.short } fn short(&self) -> Option<char> { self.short }
fn long(&self) -> Option<&'e str> { self.long } fn long(&self) -> Option<&'e str> { self.long }
fn val_delim(&self) -> Option<char> { self.val_delim } fn val_delim(&self) -> Option<char> { self.val_delim }
fn takes_value(&self) -> bool { true }
fn help(&self) -> Option<&'e str> { self.help }
fn default_val(&self) -> Option<&'n str> { self.default_val }
} }
#[cfg(test)] #[cfg(test)]

View file

@ -6,7 +6,7 @@ use std::io;
use vec_map::VecMap; use vec_map::VecMap;
use Arg; use Arg;
use args::AnyArg; use args::{AnyArg, HelpWriter};
use args::settings::{ArgFlags, ArgSettings}; use args::settings::{ArgFlags, ArgSettings};
#[allow(missing_debug_implementations)] #[allow(missing_debug_implementations)]
@ -63,7 +63,7 @@ impl<'n, 'e> PosBuilder<'n, 'e> {
} }
pub fn from_arg(a: &Arg<'n, 'e>, idx: u64, reqs: &mut Vec<&'e str>) -> Self { pub fn from_arg(a: &Arg<'n, 'e>, idx: u64, reqs: &mut Vec<&'e str>) -> Self {
assert!(a.short.is_none() || a.long.is_none(), debug_assert!(a.short.is_none() || a.long.is_none(),
format!("Argument \"{}\" has conflicting requirements, both index() and short(), \ format!("Argument \"{}\" has conflicting requirements, both index() and short(), \
or long(), were supplied", a.name)); or long(), were supplied", a.name));
@ -105,9 +105,10 @@ impl<'n, 'e> PosBuilder<'n, 'e> {
pb pb
} }
pub fn write_help<W: io::Write>(&self, w: &mut W, tab: &str, longest: usize, skip_pv: bool, nlh: bool) -> io::Result<()> { pub fn write_help<W: io::Write>(&self, w: &mut W, longest: usize, skip_pv: bool, nlh: bool) -> io::Result<()> {
write_arg_help!(@pos self, w, tab, longest, skip_pv, nlh); let mut hw = HelpWriter::new(self, longest, nlh);
write!(w, "\n") hw.skip_pv = skip_pv;
hw.write_to(w)
} }
} }
@ -139,6 +140,7 @@ impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
fn overrides(&self) -> Option<&[&'e str]> { self.overrides.as_ref().map(|o| &o[..]) } fn overrides(&self) -> Option<&[&'e str]> { self.overrides.as_ref().map(|o| &o[..]) }
fn requires(&self) -> Option<&[&'e str]> { self.requires.as_ref().map(|o| &o[..]) } fn requires(&self) -> Option<&[&'e str]> { self.requires.as_ref().map(|o| &o[..]) }
fn blacklist(&self) -> Option<&[&'e str]> { self.blacklist.as_ref().map(|o| &o[..]) } fn blacklist(&self) -> Option<&[&'e str]> { self.blacklist.as_ref().map(|o| &o[..]) }
fn val_names(&self) -> Option<&VecMap<&'e str>> { self.val_names.as_ref() }
fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) } fn is_set(&self, s: ArgSettings) -> bool { self.settings.is_set(s) }
fn set(&mut self, s: ArgSettings) { self.settings.set(s) } fn set(&mut self, s: ArgSettings) { self.settings.set(s) }
fn has_switch(&self) -> bool { false } fn has_switch(&self) -> bool { false }
@ -152,6 +154,9 @@ impl<'n, 'e> AnyArg<'n, 'e> for PosBuilder<'n, 'e> {
fn short(&self) -> Option<char> { None } fn short(&self) -> Option<char> { None }
fn long(&self) -> Option<&'e str> { None } fn long(&self) -> Option<&'e str> { None }
fn val_delim(&self) -> Option<char> { self.val_delim } fn val_delim(&self) -> Option<char> { self.val_delim }
fn takes_value(&self) -> bool { true }
fn help(&self) -> Option<&'e str> { self.help }
fn default_val(&self) -> Option<&'n str> { self.default_val }
} }
#[cfg(test)] #[cfg(test)]

254
src/args/help_writer.rs Normal file
View file

@ -0,0 +1,254 @@
use std::io;
use args::AnyArg;
use args::settings::ArgSettings;
use term;
const TAB: &'static str = " ";
pub struct HelpWriter<'a, A> where A: 'a {
a: &'a A,
l: usize,
nlh: bool,
pub skip_pv: bool,
term_w: Option<usize>,
}
impl<'a, 'n, 'e, A> HelpWriter<'a, A> where A: AnyArg<'n, 'e> {
pub fn new(a: &'a A, l: usize, nlh: bool) -> Self {
HelpWriter {
a: a,
l: l,
nlh: nlh,
skip_pv: false,
term_w: term::dimensions().map(|(w, _)| w),
}
}
pub fn write_to<W: io::Write>(&self, w: &mut W) -> io::Result<()> {
debugln!("fn=write_to;");
try!(self.short(w));
try!(self.long(w));
try!(self.val(w));
try!(self.help(w));
write!(w, "\n")
}
fn short<W>(&self, w: &mut W) -> io::Result<()>
where W: io::Write
{
debugln!("fn=short;");
try!(write!(w, "{}", TAB));
if let Some(s) = self.a.short() {
write!(w, "-{}", s)
} else if self.a.has_switch() {
write!(w, "{}", TAB)
} else {
Ok(())
}
}
fn long<W>(&self, w: &mut W) -> io::Result<()>
where W: io::Write
{
debugln!("fn=long;");
if !self.a.has_switch() {
return Ok(());
}
if self.a.takes_value() {
if let Some(l) = self.a.long() {
try!(write!(w, "{}--{}", if self.a.short().is_some() { ", " } else { "" }, l));
}
try!(write!(w, " "));
} else {
if let Some(l) = self.a.long() {
try!(write!(w, "{}--{}", if self.a.short().is_some() { ", " } else { "" }, l));
if !self.nlh || !self.a.is_set(ArgSettings::NextLineHelp) {
write_spaces!((self.l + 4) - (l.len() + 2), w);
}
} else {
if !self.nlh || !self.a.is_set(ArgSettings::NextLineHelp) {
// 6 is tab (4) + -- (2)
write_spaces!((self.l + 6), w);
}
}
}
Ok(())
}
fn val<W>(&self, w: &mut W) -> io::Result<()>
where W: io::Write
{
debugln!("fn=val;");
if !self.a.takes_value() {
return Ok(());
}
if let Some(ref vec) = self.a.val_names() {
let mut it = vec.iter().peekable();
while let Some((_, val)) = it.next() {
try!(write!(w, "<{}>", val));
if it.peek().is_some() { try!(write!(w, " ")); }
}
let num = vec.len();
if self.a.is_set(ArgSettings::Multiple) && num == 1 {
try!(write!(w, "..."));
}
} else if let Some(num) = self.a.num_vals() {
let mut it = (0..num).peekable();
while let Some(_) = it.next() {
try!(write!(w, "<{}>", self.a.name()));
if it.peek().is_some() { try!(write!(w, " ")); }
}
} else {
try!(write!(w, "<{}>{}", self.a.name(), if self.a.is_set(ArgSettings::Multiple) { "..." } else { "" }));
}
if self.a.has_switch() {
if !(self.nlh || self.a.is_set(ArgSettings::NextLineHelp)) {
let self_len = self.a.to_string().len();
// subtract ourself
let mut spcs = self.l - self_len;
// Since we're writing spaces from the tab point we first need to know if we
// had a long and short, or just short
if self.a.long().is_some() {
// Only account 4 after the val
spcs += 4;
} else {
// Only account for ', --' + 4 after the val
spcs += 8;
}
write_spaces!(spcs, w);
}
} else {
if !(self.nlh || self.a.is_set(ArgSettings::NextLineHelp)) {
write_spaces!(self.l + 4 - (self.a.to_string().len()), w);
}
}
Ok(())
}
fn help<W>(&self, w: &mut W) -> io::Result<()>
where W: io::Write
{
debugln!("fn=help;");
let spec_vals = self.spec_vals();
let mut help = String::new();
let h = self.a.help().unwrap_or("");
let spcs = if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
8 // "tab" + "tab"
} else {
self.l + 12
};
// determine if our help fits or needs to wrap
let too_long = self.term_w.is_some() && (spcs + h.len() + spec_vals.len() >= self.term_w.unwrap_or(0));
// Is help on next line, if so newline + 2x tab
if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
try!(write!(w, "\n{}{}", TAB, TAB));
}
debug!("Too long...");
if too_long {
sdebugln!("Yes");
if let Some(width) = self.term_w {
help.push_str(h);
help.push_str(&*spec_vals);
debugln!("term width: {}", width);
debugln!("help: {}", help);
debugln!("help len: {}", help.len());
// Determine how many newlines we need to insert
let avail_chars = width - spcs;
debugln!("Usable space: {}", avail_chars);
let mut indices = vec![];
let mut idx = 0;
loop {
idx += avail_chars - 1;
if idx >= help.len() { break; }
// 'a' arbitrary non space char
if help.chars().nth(idx).unwrap_or('a') != ' ' {
idx = find_idx_of_space(&*help, idx);
}
debugln!("Adding idx: {}", idx);
debugln!("At {}: {:?}", idx, help.chars().nth(idx));
indices.push(idx);
if &help[idx..].len() <= &avail_chars {
break;
}
}
for (i, idx) in indices.iter().enumerate() {
debugln!("iter;i={},idx={}", i, idx);
let j = idx+(2*i);
debugln!("removing: {}", j);
debugln!("at {}: {:?}", j, help.chars().nth(j));
help.remove(j);
help.insert(j, '{');
help.insert(j + 1 , 'n');
help.insert(j + 2, '}');
}
}
} else { sdebugln!("No"); }
let help = if !help.is_empty() {
&*help
} else if !spec_vals.is_empty() {
help.push_str(h);
help.push_str(&*spec_vals);
&*help
} else {
h
};
if help.contains("{n}") {
if let Some(part) = help.split("{n}").next() {
try!(write!(w, "{}", part));
}
for part in help.split("{n}").skip(1) {
try!(write!(w, "\n"));
if self.nlh || self.a.is_set(ArgSettings::NextLineHelp) {
try!(write!(w, "{}{}", TAB, TAB));
} else {
if self.a.has_switch() {
write_spaces!(self.l + 12, w);
} else {
write_spaces!(self.l + 8, w);
}
}
try!(write!(w, "{}", part));
}
} else {
try!(write!(w, "{}", help));
}
Ok(())
}
fn spec_vals(&self) -> String {
debugln!("fn=spec_vals;");
if let Some(ref pv) = self.a.default_val() {
debugln!("Writing defaults");
return format!(" [default: {}] {}", pv,
if !self.skip_pv {
if let Some(ref pv) = self.a.possible_vals() {
format!(" [values: {}]", pv.join(", "))
} else { "".into() }
} else { "".into() }
);
} else if !self.skip_pv {
debugln!("Writing values");
if let Some(ref pv) = self.a.possible_vals() {
debugln!("Possible vals...{:?}", pv);
return format!(" [values: {}]", pv.join(", "));
}
}
String::new()
}
}
fn find_idx_of_space(full: &str, start: usize) -> usize {
debugln!("fn=find_idx_of_space;");
let haystack = &full[..start];
debugln!("haystack: {}", haystack);
for (i, c) in haystack.chars().rev().enumerate() {
debugln!("iter;c={},i={}", c, i);
if c == ' ' {
debugln!("Found space returning start-i...{}", start - (i+1));
return start - (i+1);
}
}
0
}

View file

@ -7,6 +7,7 @@ pub use self::matched_arg::MatchedArg;
pub use self::group::ArgGroup; pub use self::group::ArgGroup;
pub use self::any_arg::AnyArg; pub use self::any_arg::AnyArg;
pub use self::settings::ArgSettings; pub use self::settings::ArgSettings;
pub use self::help_writer::HelpWriter;
mod arg; mod arg;
pub mod any_arg; pub mod any_arg;
@ -18,3 +19,4 @@ mod matched_arg;
mod group; mod group;
#[allow(dead_code)] #[allow(dead_code)]
pub mod settings; pub mod settings;
mod help_writer;

View file

@ -405,6 +405,8 @@ extern crate strsim;
extern crate ansi_term; extern crate ansi_term;
#[cfg(feature = "yaml")] #[cfg(feature = "yaml")]
extern crate yaml_rust; extern crate yaml_rust;
#[cfg(feature = "wrap_help")]
extern crate libc;
#[macro_use] #[macro_use]
extern crate bitflags; extern crate bitflags;
extern crate vec_map; extern crate vec_map;
@ -425,6 +427,7 @@ mod fmt;
mod suggestions; mod suggestions;
mod errors; mod errors;
mod osstringext; mod osstringext;
mod term;
const INTERNAL_ERROR_MSG: &'static str = "Fatal internal error. Please consider filing a bug \ const INTERNAL_ERROR_MSG: &'static str = "Fatal internal error. Please consider filing a bug \
report at https://github.com/kbknapp/clap-rs/issues"; report at https://github.com/kbknapp/clap-rs/issues";

82
src/term.rs Normal file
View file

@ -0,0 +1,82 @@
// The following was taken and adapated from exa source
// repo: https://github.com/ogham/exa
// commit: b9eb364823d0d4f9085eb220233c704a13d0f611
// license: MIT - Copyright (c) 2014 Benjamin Sago
//! System calls for getting the terminal size.
//!
//! Getting the terminal size is performed using an ioctl command that takes
//! the file handle to the terminal -- which in this case, is stdout -- and
//! populates a structure containing the values.
//!
//! The size is needed when the user wants the output formatted into columns:
//! the default grid view, or the hybrid grid-details view.
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
use std::mem::zeroed;
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
use libc::{c_int, c_ushort, c_ulong, STDOUT_FILENO};
/// The number of rows and columns of a terminal.
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
struct Winsize {
ws_row: c_ushort,
ws_col: c_ushort,
}
// Unfortunately the actual command is not standardised...
#[cfg(any(target_os = "linux", target_os = "android"))]
#[cfg(feature = "wrap_help")]
static TIOCGWINSZ: c_ulong = 0x5413;
#[cfg(any(target_os = "macos",
target_os = "ios",
target_os = "bitrig",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "netbsd",
target_os = "openbsd"))]
#[cfg(feature = "wrap_help")]
static TIOCGWINSZ: c_ulong = 0x40087468;
extern {
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
pub fn ioctl(fd: c_int, request: c_ulong, ...) -> c_int;
}
/// Runs the ioctl command. Returns (0, 0) if output is not to a terminal, or
/// there is an error. (0, 0) is an invalid size to have anyway, which is why
/// it can be used as a nil value.
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
unsafe fn get_dimensions() -> Winsize {
let mut window: Winsize = zeroed();
let result = ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut window);
if result == -1 {
zeroed()
}
else {
window
}
}
/// Query the current processes's output, returning its width and height as a
/// number of characters. Returns `None` if the output isn't to a terminal.
#[cfg(all(feature = "wrap_help", not(target_os = "windows")))]
pub fn dimensions() -> Option<(usize, usize)> {
let w = unsafe { get_dimensions() };
if w.ws_col == 0 || w.ws_row == 0 {
None
}
else {
Some((w.ws_col as usize, w.ws_row as usize))
}
}
#[cfg(any(not(feature = "wrap_help"), target_os = "windows"))]
pub fn dimensions() -> Option<(usize, usize)> {
None
}

View file

@ -93,7 +93,7 @@ Kevin K.
tests stuff tests stuff
USAGE: USAGE:
\ttest [OPTIONS] [ARGS] test [OPTIONS] [ARGS]
OPTIONS: OPTIONS:
-f, --flag some flag -f, --flag some flag
@ -125,7 +125,7 @@ Kevin K.
tests stuff tests stuff
USAGE: USAGE:
\ttest [FLAGS] [OPTIONS] [ARGS] test [FLAGS] [OPTIONS] [ARGS]
FLAGS: FLAGS:
-h, --help Prints help information -h, --help Prints help information
@ -137,24 +137,3 @@ OPTIONS:
ARGS: ARGS:
<arg1> some pos arg\n")); <arg1> some pos arg\n"));
} }
#[test]
fn app_settings_fromstr() {
assert_eq!("subcommandsnegatereqs".parse::<AppSettings>().unwrap(), AppSettings::SubcommandsNegateReqs);
assert_eq!("subcommandsrequired".parse::<AppSettings>().unwrap(), AppSettings::SubcommandRequired);
assert_eq!("argrequiredelsehelp".parse::<AppSettings>().unwrap(), AppSettings::ArgRequiredElseHelp);
assert_eq!("globalversion".parse::<AppSettings>().unwrap(), AppSettings::GlobalVersion);
assert_eq!("versionlesssubcommands".parse::<AppSettings>().unwrap(), AppSettings::VersionlessSubcommands);
assert_eq!("unifiedhelpmessage".parse::<AppSettings>().unwrap(), AppSettings::UnifiedHelpMessage);
assert_eq!("waitonerror".parse::<AppSettings>().unwrap(), AppSettings::WaitOnError);
assert_eq!("subcommandrequiredelsehelp".parse::<AppSettings>().unwrap(), AppSettings::SubcommandRequiredElseHelp);
assert_eq!("allowexternalsubcommands".parse::<AppSettings>().unwrap(), AppSettings::AllowExternalSubcommands);
assert_eq!("trailingvararg".parse::<AppSettings>().unwrap(), AppSettings::TrailingVarArg);
assert_eq!("nobinaryname".parse::<AppSettings>().unwrap(), AppSettings::NoBinaryName);
assert_eq!("strictutf8".parse::<AppSettings>().unwrap(), AppSettings::StrictUtf8);
assert_eq!("allowinvalidutf8".parse::<AppSettings>().unwrap(), AppSettings::AllowInvalidUtf8);
assert_eq!("allowleadinghyphen".parse::<AppSettings>().unwrap(), AppSettings::AllowLeadingHyphen);
assert_eq!("hidepossiblevaluesinhelp".parse::<AppSettings>().unwrap(), AppSettings::HidePossibleValuesInHelp);
assert_eq!("hidden".parse::<AppSettings>().unwrap(), AppSettings::Hidden);
assert!("hahahaha".parse::<AppSettings>().is_err());
}

View file

@ -72,7 +72,7 @@ Kevin K.
tests stuff tests stuff
USAGE: USAGE:
\ttest [FLAGS] [OPTIONS] test [FLAGS] [OPTIONS]
FLAGS: FLAGS:
-f, --flag some flag -f, --flag some flag
@ -102,7 +102,7 @@ Kevin K.
tests stuff tests stuff
USAGE: USAGE:
\ttest [FLAGS] [OPTIONS] [ARGS] test [FLAGS] [OPTIONS] [ARGS]
FLAGS: FLAGS:
-h, --help Prints help information -h, --help Prints help information

View file

@ -22,7 +22,7 @@ Kevin K.
tests stuff tests stuff
USAGE: USAGE:
\ttest [FLAGS] [OPTIONS] test [FLAGS] [OPTIONS]
FLAGS: FLAGS:
-F, --flag2 some other flag -F, --flag2 some other flag