Auto merge of #517 - kbknapp:quick-issues, r=kbknapp

Quick issues

Relates to #515 #516 #514
This commit is contained in:
Homu 2016-05-31 19:42:29 +09:00
commit 34e608c124
12 changed files with 220 additions and 65 deletions

View file

@ -1,3 +1,24 @@
<a name="v2.5.2"></a>
### v2.5.2 (2016-05-31)
#### Improvements
* removes extra newline from help output ([86e61d19](https://github.com/kbknapp/clap-rs/commit/86e61d19a748fb9870fcf1175308984e51ca1115))
* allows printing version to any io::Write object ([921f5f79](https://github.com/kbknapp/clap-rs/commit/921f5f7916597f1d028cd4a65bfe76a01c801724))
* removes extra newline when printing version ([7e2e2cbb](https://github.com/kbknapp/clap-rs/commit/7e2e2cbb4a8a0f050bb8072a376f742fc54b8589))
#### Bug Fixes
* fixes bug where args are printed out of order with templates ([3935431d](https://github.com/kbknapp/clap-rs/commit/3935431d5633f577c0826ae2142794b301f4b8ca))
* fixes bug where one can't override version or help flags ([90d7d6a2](https://github.com/kbknapp/clap-rs/commit/90d7d6a2ea8240122dd9bf8d82d3c4f5ebb5c703), closes [#514](https://github.com/kbknapp/clap-rs/issues/514))
* fixes issue where before_help wasn't printed ([b3faff60](https://github.com/kbknapp/clap-rs/commit/b3faff6030f76a23f26afcfa6a90169002ed7106))
#### Documentation
* inter-links all types and pages ([3312893d](https://github.com/kbknapp/clap-rs/commit/3312893ddaef3f44d68d8d26ed3d08010be50d97), closes [#505](https://github.com/kbknapp/clap-rs/issues/505))
* makes all publicly available types viewable in docs ([52ca6505](https://github.com/kbknapp/clap-rs/commit/52ca6505b4fec7b5c2d53d160c072d395eb21da6))
<a name="v2.5.1"></a>
### v2.5.1 (2016-05-11)
@ -6,8 +27,6 @@
* **Subcommand Aliases**: fixes lifetime issue when setting multiple aliases at once ([ac42f6cf0](https://github.com/kbknapp/clap-rs/commit/ac42f6cf0de6c4920f703807d63061803930b18d))
<a name="v2.5.0"></a>
## v2.5.0 (2016-05-10)

View file

@ -1,7 +1,7 @@
[package]
name = "clap"
version = "2.5.1"
version = "2.5.2"
authors = ["Kevin K. <kbknapp@gmail.com>"]
exclude = ["examples/*", "clap-test/*", "tests/*", "benches/*", "*.png", "clap-perf/*", "*.dot"]
description = "A simple to use, efficient, and full featured Command Line Argument Parser"

View file

@ -38,6 +38,16 @@ Created by [gh-md-toc](https://github.com/ekalinin/github-markdown-toc)
## What's New
Here's what's new in v.2.5.2
* Removes trailing newlines from help and version output
* Allows printing version to any io::Write object
* Inter-links all types and pages
* Makes all publicly available types viewable in docs
* Fixes bug where one can't override version or help flags
* Fixes bug where args are printed out of order when using templates
* Fixes issue where `App::before_help` wasn't printed properly
Here's what's new in v.2.5.0
* Subcommands now support aliases - think of them as hidden subcommands that dispatch to said subcommand automatically
@ -50,12 +60,12 @@ Here's what's new in v2.4.3
* Improvements
* Positional arguments which are part of a group are now formatted in a more readable way (fewer brackets)
* Positional arguments use the standard `<>` brackets to reduce confusion
* The default help string for the `help` subcommand has been shortened to fit in 80 columns
* The default help string for the `help` subcommand has been shortened to fit in 80 columns
Here's the highlights from v2.4.0
* **Before Help:** adds support for displaying info before help message
* **Required Unless:** adds support for allowing args that are required unless certain other args are present
* **Before Help:** adds support for displaying info before help message
* **Required Unless:** adds support for allowing args that are required unless certain other args are present
* Bug fixes
Here's the highlights from v2.3.0

View file

@ -60,6 +60,16 @@ mod test {
assert_eq!(str::from_utf8(&help).unwrap(), out);
}
pub fn check_version(mut a: App, out: &str) {
// We call a get_matches method to cause --help and --version to be built
let _ = a.get_matches_from_safe_borrow(vec![""]);
// Now we check the output of print_version()
let mut ver = vec![];
a.write_version(&mut ver).ok().expect("failed to print help");
assert_eq!(str::from_utf8(&ver).unwrap(), out);
}
pub fn check_complex_output(args: &str, out: &str) {
let mut w = vec![];
let matches = complex_app().get_matches_from(args.split(' ').collect::<Vec<_>>());

View file

@ -88,6 +88,7 @@ pub struct Help<'a> {
impl<'a> Help<'a> {
/// Create a new `Help` instance.
pub fn new(w: &'a mut Write, next_line_help: bool, hide_pv: bool, color: bool) -> Self {
debugln!("fn=Help::new;");
Help {
writer: w,
next_line_help: next_line_help,
@ -100,12 +101,14 @@ impl<'a> Help<'a> {
/// Reads help settings from an App
/// and write its help to the wrapped stream.
pub fn write_app_help(w: &'a mut Write, app: &App) -> ClapResult<()> {
debugln!("fn=Help::write_app_help;");
Self::write_parser_help(w, &app.p)
}
/// Reads help settings from a Parser
/// and write its help to the wrapped stream.
pub fn write_parser_help(w: &'a mut Write, parser: &Parser) -> ClapResult<()> {
debugln!("fn=Help::write_parser_help;");
let nlh = parser.is_set(AppSettings::NextLineHelp);
let hide_v = parser.is_set(AppSettings::HidePossibleValuesInHelp);
let color = parser.is_set(AppSettings::ColoredHelp);
@ -114,8 +117,9 @@ impl<'a> Help<'a> {
/// Writes the parser help to the wrapped stream.
pub fn write_help(&mut self, parser: &Parser) -> ClapResult<()> {
debugln!("fn=Help::write_help;");
if let Some(h) = parser.meta.help_str {
try!(writeln!(self.writer, "{}", h).map_err(Error::from));
try!(write!(self.writer, "{}", h).map_err(Error::from));
} else if let Some(ref tmpl) = parser.meta.template {
try!(self.write_templated_help(&parser, tmpl));
} else {
@ -131,6 +135,7 @@ impl<'a> Help<'a> {
fn write_args_unsorted<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
where I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>
{
debugln!("fn=write_args_unsorted;");
let mut longest = 0;
let mut arg_v = Vec::with_capacity(10);
for arg in args.filter(|arg| {
@ -143,7 +148,13 @@ impl<'a> Help<'a> {
arg_v.push(arg)
}
}
let mut first = true;
for arg in arg_v {
if !first {
try!(self.writer.write(b"\n"));
} else {
first = false;
};
try!(self.write_arg(arg.as_base(), longest));
}
Ok(())
@ -153,6 +164,7 @@ impl<'a> Help<'a> {
fn write_args<'b: 'd, 'c: 'd, 'd, I: 'd>(&mut self, args: I) -> io::Result<()>
where I: Iterator<Item = &'d ArgWithOrder<'b, 'c>>
{
debugln!("fn=write_args;");
let mut longest = 0;
let mut ord_m = VecMap::new();
for arg in args.filter(|arg| {
@ -166,8 +178,14 @@ impl<'a> Help<'a> {
btm.insert(arg.name(), arg);
}
}
let mut first = true;
for (_, btm) in ord_m.into_iter() {
for (_, arg) in btm.into_iter() {
if !first {
try!(self.writer.write(b"\n"));
} else {
first = false;
}
try!(self.write_arg(arg.as_base(), longest));
}
}
@ -179,12 +197,11 @@ impl<'a> Help<'a> {
arg: &ArgWithDisplay<'b, 'c>,
longest: usize)
-> io::Result<()> {
debugln!("fn=write_to;");
debugln!("fn=write_arg;");
try!(self.short(arg));
try!(self.long(arg, longest));
try!(self.val(arg, longest));
try!(self.help(arg, longest));
try!(self.writer.write(b"\n"));
Ok(())
}
@ -458,45 +475,33 @@ impl<'a> Help<'a> {
let unified_help = parser.is_set(AppSettings::UnifiedHelpMessage);
let mut first = true;
if unified_help && (flags || opts) {
let opts_flags = parser.iter_flags()
.map(as_arg_trait)
.chain(parser.iter_opts().map(as_arg_trait));
try!(color!(self, "OPTIONS:\n", Warning));
try!(self.write_args(opts_flags));
first = false;
} else {
if flags {
try!(color!(self, "FLAGS:\n", Warning));
try!(self.write_args(parser.iter_flags()
.map(as_arg_trait)));
first = false;
}
if opts {
if !first {
try!(self.writer.write(b"\n"));
}
try!(self.writer.write(b"\n\n"));
try!(color!(self, "OPTIONS:\n", Warning));
try!(self.write_args(parser.iter_opts().map(as_arg_trait)));
first = false;
}
}
if pos {
if !first {
try!(self.writer.write(b"\n"));
}
try!(self.writer.write(b"\n\n"));
try!(color!(self, "ARGS:\n", Warning));
try!(self.write_args_unsorted(parser.iter_positionals().map(as_arg_trait)));
first = false;
}
if subcmds {
if !first {
try!(self.writer.write(b"\n"));
}
try!(self.writer.write(b"\n\n"));
try!(color!(self, "SUBCOMMANDS:\n", Warning));
try!(self.write_subcommands(&parser));
}
@ -506,6 +511,7 @@ impl<'a> Help<'a> {
/// Writes help for subcommands of a Parser Object to the wrapped stream.
fn write_subcommands(&mut self, parser: &Parser) -> io::Result<()> {
debugln!("exec=write_subcommands;");
let mut longest = 0;
let mut ord_m = VecMap::new();
@ -515,8 +521,16 @@ impl<'a> Help<'a> {
longest = cmp::max(longest, sc.p.meta.name.len());
}
let mut first = true;
for (_, btm) in ord_m.into_iter() {
for (_, sc) in btm.into_iter() {
if !first {
debugln!("Writing newline...");
try!(self.writer.write(b"\n"));
} else {
first = false;
}
debugln!("Writing sc...{}", sc);
try!(self.write_arg(sc, longest));
}
}
@ -546,6 +560,11 @@ impl<'a> Help<'a> {
/// Writes default help for a Parser Object to the wrapped stream.
pub fn write_default_help(&mut self, parser: &Parser) -> ClapResult<()> {
debugln!("fn=write_default_help;");
if let Some(h) = parser.meta.pre_help {
try!(write!(self.writer, "{}", h));
try!(self.writer.write(b"\n\n"));
}
// Print the version
try!(self.write_bin_name(&parser));
@ -575,7 +594,10 @@ impl<'a> Help<'a> {
}
if let Some(h) = parser.meta.more_help {
try!(write!(self.writer, "{}\n", h));
if flags || opts || pos || subcmds {
try!(self.writer.write(b"\n\n"));
}
try!(write!(self.writer, "{}", h));
}
self.writer.flush().map_err(Error::from)
@ -708,6 +730,7 @@ impl<'a> Help<'a> {
/// The template system is, on purpose, very simple. Therefore the tags have to writen
/// in the lowercase and without spacing.
fn write_templated_help(&mut self, parser: &Parser, template: &str) -> ClapResult<()> {
debugln!("fn=write_templated_help;");
let mut tmplr = Cursor::new(&template);
let mut tag_buf = Cursor::new(vec![0u8; 15]);
@ -725,6 +748,12 @@ impl<'a> Help<'a> {
_ => continue,
};
debugln!("iter;tag_buf={};", unsafe {
String::from_utf8_unchecked(tag_buf.get_ref()[0..tag_length]
.iter()
.map(|&i|i)
.collect::<Vec<_>>())
});
match &tag_buf.get_ref()[0..tag_length] {
b"?" => {
try!(self.writer.write(b"Could not decode tag name"));
@ -768,8 +797,8 @@ impl<'a> Help<'a> {
.map(as_arg_trait)));
}
b"positionals" => {
try!(self.write_args(parser.iter_positionals()
.map(as_arg_trait)));
try!(self.write_args_unsorted(parser.iter_positionals()
.map(as_arg_trait)));
}
b"subcommands" => {
try!(self.write_subcommands(&parser));
@ -791,7 +820,6 @@ impl<'a> Help<'a> {
try!(self.writer.write(b"}"));
}
}
}
}
}

View file

@ -796,6 +796,22 @@ impl<'a, 'b> App<'a, 'b> {
Help::write_app_help(w, &self)
}
/// Writes the version message to the user to a [`io::Write`] object
///
/// # Examples
///
/// ```rust
/// # use clap::App;
/// use std::io;
/// let mut app = App::new("myprog");
/// let mut out = io::stdout();
/// app.write_version(&mut out).ok().expect("failed to write to stdout");
/// ```
/// [`io::Write`]: https://doc.rust-lang.org/std/io/trait.Write.html
pub fn write_version<W: Write>(&self, w: &mut W) -> ClapResult<()> {
self.p.write_version(w).map_err(From::from)
}
/// Starts the parsing process, upon a failed parse an error will be displayed to the user and
/// the process with exit with the appropriate error code. By default this method gets all user
/// provided arguments from [`env::args_os`] in order to allow for invalid UTF-8 code points,

View file

@ -115,9 +115,9 @@ impl<'a, 'b> Parser<'a, 'b>
l));
self.long_list.push(l);
if l == "help" {
self.set(AppSettings::NeedsLongHelp);
self.unset(AppSettings::NeedsLongHelp);
} else if l == "version" {
self.set(AppSettings::NeedsLongVersion);
self.unset(AppSettings::NeedsLongVersion);
}
}
if a.is_set(ArgSettings::Required) {
@ -378,6 +378,10 @@ impl<'a, 'b> Parser<'a, 'b>
self.settings.set(s)
}
pub fn unset(&mut self, s: AppSettings) {
self.settings.unset(s)
}
pub fn verify_positionals(&mut self) {
// Because you must wait until all arguments have been supplied, this is the first chance
// to make assertions on positional argument indexes
@ -929,13 +933,13 @@ impl<'a, 'b> Parser<'a, 'b>
debug!("Checking if -{} is help or version...", arg);
if let Some(h) = self.help_short {
sdebugln!("Help");
if arg == h {
if arg == h && self.settings.is_set(AppSettings::NeedsLongHelp) {
try!(self._help());
}
}
if let Some(v) = self.version_short {
sdebugln!("Help");
if arg == v {
if arg == v && self.settings.is_set(AppSettings::NeedsLongVersion) {
try!(self._version());
}
}
@ -1542,22 +1546,22 @@ impl<'a, 'b> Parser<'a, 'b>
w.flush().map_err(Error::from)
}
fn write_version<W: Write>(&self, w: &mut W) -> io::Result<()> {
pub fn write_version<W: Write>(&self, w: &mut W) -> io::Result<()> {
if let Some(bn) = self.meta.bin_name.as_ref() {
if bn.contains(' ') {
// Incase we're dealing with subcommands i.e. git mv is translated to git-mv
writeln!(w,
write!(w,
"{} {}",
bn.replace(" ", "-"),
self.meta.version.unwrap_or("".into()))
} else {
writeln!(w,
write!(w,
"{} {}",
&self.meta.name[..],
self.meta.version.unwrap_or("".into()))
}
} else {
writeln!(w,
write!(w,
"{} {}",
&self.meta.name[..],
self.meta.version.unwrap_or("".into()))
@ -1570,7 +1574,6 @@ impl<'a, 'b> Parser<'a, 'b>
self.write_help(&mut buf_w)
}
#[cfg_attr(feature = "lints", allow(for_kv_map))]
pub fn write_help<W: Write>(&self, w: &mut W) -> ClapResult<()> {
Help::write_parser_help(w, &self)
}

View file

@ -102,7 +102,7 @@ OPTIONS:
-V, --version Prints version information
ARGS:
<arg1> some pos arg\n"));
<arg1> some pos arg"));
}
#[test]
@ -135,5 +135,5 @@ OPTIONS:
-o, --opt <opt> some option
ARGS:
<arg1> some pos arg\n"));
<arg1> some pos arg"));
}

View file

@ -34,8 +34,21 @@ ARGS:
SUBCOMMANDS:
help Prints this message or the help of the given subcommand(s)
subcmd tests subcommands
";
subcmd tests subcommands";
static AFTER_HELP: &'static str = "some text that comes before the help
clap-test v1.4.8
tests clap library
USAGE:
clap-test [FLAGS]
FLAGS:
-h, --help Prints help information
-V, --version Prints version information
some text that comes after the help";
static SC_HELP: &'static str = "subcmd 0.1
Kevin K. <kbknapp@gmail.com>
@ -51,8 +64,7 @@ OPTIONS:
-o, --option <scoption>... tests options
ARGS:
<scpositional> tests positionals
";
<scpositional> tests positionals";
#[test]
fn help_short() {
@ -137,6 +149,16 @@ fn complex_help_output() {
test::check_help(test::complex_app(), HELP);
}
#[test]
fn after_and_before_help_output() {
let app = App::new("clap-test")
.version("v1.4.8")
.about("tests clap library")
.before_help("some text that comes before the help")
.after_help("some text that comes after the help");
test::check_help(app, AFTER_HELP);
}
#[test]
fn complex_subcommand_help_output() {
let mut a = test::complex_app();

View file

@ -30,5 +30,5 @@ FLAGS:
-V, --version Prints version information
OPTIONS:
--option <opt> some option\n"));
--option <opt> some option"));
}

View file

@ -7,6 +7,41 @@ use clap::{App, SubCommand};
static EXAMPLE1_TMPL_S : &'static str = include_str!("example1_tmpl_simple.txt");
static EXAMPLE1_TMPS_F : &'static str = include_str!("example1_tmpl_full.txt");
static CUSTOM_TEMPL_HELP: &'static str = "MyApp 1.0
Kevin K. <kbknapp@gmail.com>
Does awesome things
USAGE:
MyApp [FLAGS] [OPTIONS] <output> [SUBCOMMAND]
FLAGS:
-d Turn debugging information on
OPTIONS:
-c, --config <FILE> Sets a custom config file
ARGS:
<output> Sets an optional output file
SUBCOMMANDS:
test does testing things";
static SIMPLE_TEMPLATE: &'static str = "MyApp 1.0
Kevin K. <kbknapp@gmail.com>
Does awesome things
USAGE:
MyApp [FLAGS] [OPTIONS] <output> [SUBCOMMAND]
FLAGS:
-d Turn debugging information on
OPTIONS:
-c, --config <FILE> Sets a custom config file
ARGS:
<output> Sets an optional output file
SUBCOMMANDS:
test does testing things";
fn build_new_help(app: &App) -> String {
let mut buf = Cursor::new(Vec::with_capacity(50));
app.write_help(&mut buf).unwrap();
@ -14,27 +49,29 @@ fn build_new_help(app: &App) -> String {
String::from_utf8(content).unwrap()
}
fn compare2(app1: &App, app2: &App) -> bool {
let hlp1f = build_new_help(&app1);
let hlp1 = hlp1f.trim();
let hlp2f = build_new_help(&app2);
let hlp2 = hlp2f.trim();
let b = hlp1 == hlp2;
fn compare_app_str(l: &App, right: &str) -> bool {
let left = build_new_help(&l);
let b = left.trim() == right;
if !b {
println!("");
println!("--> hlp1");
println!("{}", hlp1);
println!("--> hlp2");
println!("{}", hlp2);
println!("--> left");
println!("{}", left);
println!("--> right");
println!("{}", right);
println!("--")
}
b
}
#[test]
fn comparison_with_template() {
assert!(compare2(&app_example1(), &app_example1().template(EXAMPLE1_TMPL_S)));
assert!(compare2(&app_example1(), &app_example1().template(EXAMPLE1_TMPS_F)));
fn with_template() {
assert!(compare_app_str(&app_example1().template(EXAMPLE1_TMPL_S), SIMPLE_TEMPLATE));
}
#[test]
fn custom_template() {
let app = app_example1().template(EXAMPLE1_TMPS_F);
assert!(compare_app_str(&app, CUSTOM_TEMPL_HELP));
}
#[test]
@ -44,7 +81,7 @@ fn template_empty() {
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.template("");
assert_eq!(build_new_help(&app), "");
assert!(compare_app_str(&app, ""));
}
#[test]
@ -53,8 +90,8 @@ fn template_notag() {
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.template(" no tag ");
assert_eq!(build_new_help(&app), " no tag ");
.template("test no tag test");
assert!(compare_app_str(&app, "test no tag test"));
}
#[test]
@ -63,8 +100,8 @@ fn template_unknowntag() {
.version("1.0")
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.template(" {unknown_tag} ");
assert_eq!(build_new_help(&app), " {unknown_tag} ");
.template("test {unknown_tag} test");
assert!(compare_app_str(&app, "test {unknown_tag} test"));
}
#[test]
@ -74,7 +111,7 @@ fn template_author_version() {
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.template("{author}\n{version}\n{about}\n{bin}");
assert_eq!(build_new_help(&app), "Kevin K. <kbknapp@gmail.com>\n1.0\nDoes awesome things\nMyApp");
assert!(compare_app_str(&app, "Kevin K. <kbknapp@gmail.com>\n1.0\nDoes awesome things\nMyApp"));
}
fn app_example1<'b, 'c>() -> App<'b, 'c> {
@ -83,8 +120,8 @@ fn app_example1<'b, 'c>() -> App<'b, 'c> {
.author("Kevin K. <kbknapp@gmail.com>")
.about("Does awesome things")
.args_from_usage("-c, --config=[FILE] 'Sets a custom config file'
<output> 'Sets an optional output file'
-d... 'Turn debugging information on'")
<output> 'Sets an optional output file'
-d... 'Turn debugging information on'")
.subcommand(SubCommand::with_name("test")
.about("does testing things")
.arg_from_usage("-l, --list 'lists test values'"))

View file

@ -1,7 +1,12 @@
extern crate clap;
extern crate regex;
use clap::{App, ErrorKind};
include!("../clap-test.rs");
static VERSION: &'static str = "clap-test v1.4.8";
#[test]
fn version_short() {
let m = App::new("test")
@ -25,3 +30,8 @@ fn version_long() {
assert!(m.is_err());
assert_eq!(m.unwrap_err().kind, ErrorKind::VersionDisplayed);
}
#[test]
fn complex_version_output() {
test::check_version(test::complex_app(), VERSION);
}