Auto merge of #619 - kbknapp:issue-618, r=kbknapp

Issue 618
This commit is contained in:
Homu 2016-08-21 07:13:07 +09:00
commit 37cc3c367f
6 changed files with 66 additions and 32 deletions

View file

@ -9,6 +9,7 @@ rust:
matrix:
allow_failures:
- rust: nightly
- rust: beta
before_script:
- |
pip install 'travis-cargo<0.2' --user &&

View file

@ -150,7 +150,7 @@ impl<'a> Help<'a> {
debugln!("fn=Help::write_help;");
if let Some(h) = parser.meta.help_str {
try!(write!(self.writer, "{}", h).map_err(Error::from));
} else if let Some(ref tmpl) = parser.meta.template {
} else if let Some(tmpl) = parser.meta.template {
try!(self.write_templated_help(&parser, tmpl));
} else {
try!(self.write_default_help(&parser));
@ -282,7 +282,7 @@ impl<'a> Help<'a> {
if !arg.takes_value() {
return Ok(());
}
if let Some(ref vec) = arg.val_names() {
if let Some(vec) = arg.val_names() {
let mut it = vec.iter().peekable();
while let Some((_, val)) = it.next() {
try!(color!(self, "<{}>", val, good));
@ -524,7 +524,7 @@ impl<'a> Help<'a> {
fn spec_vals(&self, a: &ArgWithDisplay) -> String {
debugln!("fn=spec_vals;");
if let Some(ref pv) = a.default_val() {
if let Some(pv) = a.default_val() {
debugln!("Writing defaults");
return format!(" [default: {}] {}",
if self.color {
@ -562,7 +562,7 @@ impl<'a> Help<'a> {
});
} else if !self.hide_pv {
debugln!("Writing values");
if let Some(ref pv) = a.possible_vals() {
if let Some(pv) = a.possible_vals() {
debugln!("Possible vals...{:?}", pv);
return if self.color {
format!(" [values: {}]",
@ -943,7 +943,7 @@ impl<'a> Help<'a> {
parser.meta.pre_help.unwrap_or("unknown before-help")));
}
// Unknown tag, write it back.
ref r => {
r => {
try!(self.writer.write(b"{"));
try!(self.writer.write(r));
try!(self.writer.write(b"}"));

View file

@ -235,6 +235,7 @@ impl<'a, 'b> Parser<'a, 'b>
} else {
sdebugln!("No");
}
debug!("Using Setting VersionlessSubcommands...");
if self.settings.is_set(AppSettings::VersionlessSubcommands) {
sdebugln!("Yes");
@ -495,7 +496,7 @@ impl<'a, 'b> Parser<'a, 'b>
// Firt we verify that the index highest supplied index, is equal to the number of
// positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3
// but no 2)
if let Some((idx, ref p)) = self.positionals.iter().rev().next() {
if let Some((idx, p)) = self.positionals.iter().rev().next() {
debug_assert!(!(idx != self.positionals.len()),
format!("Found positional argument \"{}\" who's index is {} but there are \
only {} positional arguments defined",
@ -620,6 +621,10 @@ impl<'a, 'b> Parser<'a, 'b>
self.settings.is_set(AppSettings::NeedsSubcommandHelp) {
let cmds: Vec<OsString> = it.map(|c| c.into()).collect();
let mut help_help = false;
let mut bin_name = self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name).clone();
let mut sc = {
let mut sc: &Parser = self;
for (i, cmd) in cmds.iter().enumerate() {
@ -663,6 +668,9 @@ impl<'a, 'b> Parser<'a, 'b>
.unwrap_or(&self.meta.name),
self.color()));
}
bin_name = format!("{} {}",
bin_name,
&*sc.meta.name);
}
sc.clone()
};
@ -678,17 +686,7 @@ impl<'a, 'b> Parser<'a, 'b>
sc.create_help_and_version();
}
if sc.meta.bin_name != self.meta.bin_name {
sc.meta.bin_name = Some(format!("{}{}{}",
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name.clone()),
if self.meta.bin_name.is_some() {
" "
} else {
""
},
&*sc.meta.name));
sc.meta.bin_name = Some(format!("{} {}", bin_name, sc.meta.name));
}
return sc._help();
}
@ -756,7 +754,7 @@ impl<'a, 'b> Parser<'a, 'b>
if let Some(o) = self.opts.iter().filter(|o| &o.name == &a).next() {
try!(self.validate_required(matcher));
reqs_validated = true;
let should_err = if let Some(ref v) = matcher.0.args.get(&*o.name) {
let should_err = if let Some(v) = matcher.0.args.get(&*o.name) {
v.vals.is_empty() && !(o.min_vals.is_some() && o.min_vals.unwrap() == 0)
} else {
true
@ -1154,9 +1152,13 @@ impl<'a, 'b> Parser<'a, 'b>
}
fn _help(&self) -> ClapResult<()> {
try!(self.print_help());
let mut buf = vec![];
try!(Help::write_parser_help(&mut buf, self));
let out = io::stdout();
let mut out_buf = BufWriter::new(out.lock());
try!(out_buf.write(&*buf));
Err(Error {
message: String::new(),
message: unsafe { String::from_utf8_unchecked(buf) },
kind: ErrorKind::HelpDisplayed,
info: None,
})
@ -1387,7 +1389,7 @@ impl<'a, 'b> Parser<'a, 'b>
if self.is_set(AppSettings::StrictUtf8) && val.to_str().is_none() {
return Err(Error::invalid_utf8(&*self.create_current_usage(matcher), self.color()));
}
if let Some(ref p_vals) = arg.possible_vals() {
if let Some(p_vals) = arg.possible_vals() {
let val_str = val.to_string_lossy();
if !p_vals.contains(&&*val_str) {
return Err(Error::invalid_value(val_str,
@ -1401,7 +1403,7 @@ impl<'a, 'b> Parser<'a, 'b>
matcher.contains(&*arg.name()) {
return Err(Error::empty_value(arg, &*self.create_current_usage(matcher), self.color()));
}
if let Some(ref vtor) = arg.validator() {
if let Some(vtor) = arg.validator() {
if let Err(e) = vtor(val.to_string_lossy().into_owned()) {
return Err(Error::value_validation(e, self.color()));
}
@ -1591,6 +1593,7 @@ impl<'a, 'b> Parser<'a, 'b>
self._help().unwrap_err()
} else {
let mut reqs = self.required.iter().map(|&r| &*r).collect::<Vec<_>>();
reqs.retain(|n| !matcher.contains(n));
reqs.dedup();
Error::missing_required_argument(
&*self.get_required_from(&*reqs, Some(matcher))

View file

@ -111,7 +111,7 @@ impl<'a> ArgMatches<'a> {
/// [`ArgMatches::values_of`]: ./struct.ArgMatches.html#method.values_of
/// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html
pub fn value_of<S: AsRef<str>>(&self, name: S) -> Option<&str> {
if let Some(ref arg) = self.args.get(name.as_ref()) {
if let Some(arg) = self.args.get(name.as_ref()) {
if let Some(v) = arg.vals.values().nth(0) {
return Some(v.to_str().expect(INVALID_UTF8));
}
@ -208,7 +208,7 @@ impl<'a> ArgMatches<'a> {
/// [`Values`]: ./struct.Values.html
/// [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.html
pub fn values_of<S: AsRef<str>>(&'a self, name: S) -> Option<Values<'a>> {
if let Some(ref arg) = self.args.get(name.as_ref()) {
if let Some(arg) = self.args.get(name.as_ref()) {
fn to_str_slice(o: &OsString) -> &str {
o.to_str().expect(INVALID_UTF8)
}
@ -240,7 +240,7 @@ impl<'a> ArgMatches<'a> {
/// assert_eq!(itr.next(), None);
/// ```
pub fn values_of_lossy<S: AsRef<str>>(&'a self, name: S) -> Option<Vec<String>> {
if let Some(ref arg) = self.args.get(name.as_ref()) {
if let Some(arg) = self.args.get(name.as_ref()) {
return Some(arg.vals
.values()
.map(|v| v.to_string_lossy().into_owned())
@ -284,7 +284,7 @@ impl<'a> ArgMatches<'a> {
&*o
}
let to_str_slice: fn(&'a OsString) -> &'a OsStr = to_str_slice; // coerce to fn pointer
if let Some(ref arg) = self.args.get(name.as_ref()) {
if let Some(arg) = self.args.get(name.as_ref()) {
return Some(OsValues { iter: arg.vals.values().map(to_str_slice) });
}
None

View file

@ -211,7 +211,7 @@ end
};
let mut buffer = detect_subcommand_function;
gen_fish_inner(command, &self, vec![], &mut buffer, has_subcommands);
gen_fish_inner(command, self, vec![], &mut buffer, has_subcommands);
w!(buf, buffer.as_bytes());
}
}
@ -235,7 +235,7 @@ pub fn get_all_subcommands(p: &Parser) -> Vec<String> {
}
subcmds.push(sc.p.meta.name.clone());
}
for sc_v in p.subcommands.iter().map(|ref s| get_all_subcommands(&s.p)) {
for sc_v in p.subcommands.iter().map(|s| get_all_subcommands(&s.p)) {
subcmds.extend(sc_v);
}
subcmds.sort();
@ -269,7 +269,7 @@ pub fn get_all_subcommand_paths(p: &Parser, first: bool) -> Vec<String> {
}
}
}
for sc_v in p.subcommands.iter().map(|ref s| get_all_subcommand_paths(&s.p, false)) {
for sc_v in p.subcommands.iter().map(|s| get_all_subcommand_paths(&s.p, false)) {
subcmds.extend(sc_v);
}
subcmds
@ -279,10 +279,10 @@ fn vals_for(o: &OptBuilder) -> String {
use args::AnyArg;
let mut ret = String::new();
let mut needs_quotes = true;
if let Some(ref vals) = o.possible_vals() {
if let Some(vals) = o.possible_vals() {
needs_quotes = false;
ret = format!("$(compgen -W \"{}\" -- ${{cur}})", vals.join(" "));
} else if let Some(ref vec) = o.val_names() {
} else if let Some(vec) = o.val_names() {
let mut it = vec.iter().peekable();
while let Some((_, val)) = it.next() {
ret = format!("{}<{}>{}", ret, val,
@ -321,7 +321,7 @@ fn vals_for(o: &OptBuilder) -> String {
ret
}
fn gen_fish_inner(root_command: &String,
fn gen_fish_inner(root_command: &str,
comp_gen: &ComplGen,
parent_cmds: Vec<&String>,
buffer: &mut String,

View file

@ -66,6 +66,21 @@ OPTIONS:
ARGS:
<scpositional> tests positionals";
static MULTI_SC_HELP: &'static str = "ctest-subcmd-multi 0.1
Kevin K. <kbknapp@gmail.com>
tests subcommands
USAGE:
ctest subcmd multi [FLAGS] [OPTIONS]
FLAGS:
-f, --flag tests flags
-h, --help Prints help information
-V, --version Prints version information
OPTIONS:
-o, --option <scoption>... tests options";
#[test]
fn help_short() {
let m = App::new("test")
@ -159,6 +174,21 @@ fn after_and_before_help_output() {
test::check_help(app, AFTER_HELP);
}
#[test]
fn multi_level_sc_help() {
let app = App::new("ctest")
.subcommand(SubCommand::with_name("subcmd")
.subcommand(SubCommand::with_name("multi")
.about("tests subcommands")
.author("Kevin K. <kbknapp@gmail.com>")
.version("0.1")
.args_from_usage("
-f, --flag 'tests flags'
-o, --option [scoption]... 'tests options'
")));
test::check_err_output(app, "ctest help subcmd multi", MULTI_SC_HELP, false);
}
#[test]
fn complex_subcommand_help_output() {
let mut a = test::complex_app();