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: matrix:
allow_failures: allow_failures:
- rust: nightly - rust: nightly
- rust: beta
before_script: before_script:
- | - |
pip install 'travis-cargo<0.2' --user && pip install 'travis-cargo<0.2' --user &&

View file

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

View file

@ -235,6 +235,7 @@ impl<'a, 'b> Parser<'a, 'b>
} else { } else {
sdebugln!("No"); sdebugln!("No");
} }
debug!("Using Setting VersionlessSubcommands..."); debug!("Using Setting VersionlessSubcommands...");
if self.settings.is_set(AppSettings::VersionlessSubcommands) { if self.settings.is_set(AppSettings::VersionlessSubcommands) {
sdebugln!("Yes"); 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 // 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, ref p)) = self.positionals.iter().rev().next() { if let Some((idx, p)) = self.positionals.iter().rev().next() {
debug_assert!(!(idx != self.positionals.len()), debug_assert!(!(idx != self.positionals.len()),
format!("Found positional argument \"{}\" who's index is {} but there are \ format!("Found positional argument \"{}\" who's index is {} but there are \
only {} positional arguments defined", only {} positional arguments defined",
@ -620,6 +621,10 @@ impl<'a, 'b> Parser<'a, 'b>
self.settings.is_set(AppSettings::NeedsSubcommandHelp) { self.settings.is_set(AppSettings::NeedsSubcommandHelp) {
let cmds: Vec<OsString> = it.map(|c| c.into()).collect(); let cmds: Vec<OsString> = it.map(|c| c.into()).collect();
let mut help_help = false; 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 = {
let mut sc: &Parser = self; let mut sc: &Parser = self;
for (i, cmd) in cmds.iter().enumerate() { for (i, cmd) in cmds.iter().enumerate() {
@ -663,6 +668,9 @@ impl<'a, 'b> Parser<'a, 'b>
.unwrap_or(&self.meta.name), .unwrap_or(&self.meta.name),
self.color())); self.color()));
} }
bin_name = format!("{} {}",
bin_name,
&*sc.meta.name);
} }
sc.clone() sc.clone()
}; };
@ -678,17 +686,7 @@ impl<'a, 'b> Parser<'a, 'b>
sc.create_help_and_version(); sc.create_help_and_version();
} }
if sc.meta.bin_name != self.meta.bin_name { if sc.meta.bin_name != self.meta.bin_name {
sc.meta.bin_name = Some(format!("{}{}{}", sc.meta.bin_name = Some(format!("{} {}", bin_name, sc.meta.name));
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name.clone()),
if self.meta.bin_name.is_some() {
" "
} else {
""
},
&*sc.meta.name));
} }
return sc._help(); 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() { if let Some(o) = self.opts.iter().filter(|o| &o.name == &a).next() {
try!(self.validate_required(matcher)); try!(self.validate_required(matcher));
reqs_validated = true; 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) v.vals.is_empty() && !(o.min_vals.is_some() && o.min_vals.unwrap() == 0)
} else { } else {
true true
@ -1154,9 +1152,13 @@ impl<'a, 'b> Parser<'a, 'b>
} }
fn _help(&self) -> ClapResult<()> { 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 { Err(Error {
message: String::new(), message: unsafe { String::from_utf8_unchecked(buf) },
kind: ErrorKind::HelpDisplayed, kind: ErrorKind::HelpDisplayed,
info: None, info: None,
}) })
@ -1387,7 +1389,7 @@ impl<'a, 'b> Parser<'a, 'b>
if self.is_set(AppSettings::StrictUtf8) && val.to_str().is_none() { if self.is_set(AppSettings::StrictUtf8) && val.to_str().is_none() {
return Err(Error::invalid_utf8(&*self.create_current_usage(matcher), self.color())); 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(); let val_str = val.to_string_lossy();
if !p_vals.contains(&&*val_str) { if !p_vals.contains(&&*val_str) {
return Err(Error::invalid_value(val_str, return Err(Error::invalid_value(val_str,
@ -1401,7 +1403,7 @@ impl<'a, 'b> Parser<'a, 'b>
matcher.contains(&*arg.name()) { matcher.contains(&*arg.name()) {
return Err(Error::empty_value(arg, &*self.create_current_usage(matcher), self.color())); 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()) { if let Err(e) = vtor(val.to_string_lossy().into_owned()) {
return Err(Error::value_validation(e, self.color())); return Err(Error::value_validation(e, self.color()));
} }
@ -1591,6 +1593,7 @@ impl<'a, 'b> Parser<'a, 'b>
self._help().unwrap_err() self._help().unwrap_err()
} else { } else {
let mut reqs = self.required.iter().map(|&r| &*r).collect::<Vec<_>>(); let mut reqs = self.required.iter().map(|&r| &*r).collect::<Vec<_>>();
reqs.retain(|n| !matcher.contains(n));
reqs.dedup(); reqs.dedup();
Error::missing_required_argument( Error::missing_required_argument(
&*self.get_required_from(&*reqs, Some(matcher)) &*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 /// [`ArgMatches::values_of`]: ./struct.ArgMatches.html#method.values_of
/// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html /// [`panic!`]: https://doc.rust-lang.org/std/macro.panic!.html
pub fn value_of<S: AsRef<str>>(&self, name: S) -> Option<&str> { 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) { if let Some(v) = arg.vals.values().nth(0) {
return Some(v.to_str().expect(INVALID_UTF8)); return Some(v.to_str().expect(INVALID_UTF8));
} }
@ -208,7 +208,7 @@ impl<'a> ArgMatches<'a> {
/// [`Values`]: ./struct.Values.html /// [`Values`]: ./struct.Values.html
/// [`Iterator`]: https://doc.rust-lang.org/std/iter/trait.Iterator.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>> { 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 { fn to_str_slice(o: &OsString) -> &str {
o.to_str().expect(INVALID_UTF8) o.to_str().expect(INVALID_UTF8)
} }
@ -240,7 +240,7 @@ impl<'a> ArgMatches<'a> {
/// assert_eq!(itr.next(), None); /// assert_eq!(itr.next(), None);
/// ``` /// ```
pub fn values_of_lossy<S: AsRef<str>>(&'a self, name: S) -> Option<Vec<String>> { 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 return Some(arg.vals
.values() .values()
.map(|v| v.to_string_lossy().into_owned()) .map(|v| v.to_string_lossy().into_owned())
@ -284,7 +284,7 @@ impl<'a> ArgMatches<'a> {
&*o &*o
} }
let to_str_slice: fn(&'a OsString) -> &'a OsStr = to_str_slice; // coerce to fn pointer 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) }); return Some(OsValues { iter: arg.vals.values().map(to_str_slice) });
} }
None None

View file

@ -211,7 +211,7 @@ end
}; };
let mut buffer = detect_subcommand_function; 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()); 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()); 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.extend(sc_v);
} }
subcmds.sort(); 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.extend(sc_v);
} }
subcmds subcmds
@ -279,10 +279,10 @@ fn vals_for(o: &OptBuilder) -> String {
use args::AnyArg; use args::AnyArg;
let mut ret = String::new(); let mut ret = String::new();
let mut needs_quotes = true; let mut needs_quotes = true;
if let Some(ref vals) = o.possible_vals() { if let Some(vals) = o.possible_vals() {
needs_quotes = false; needs_quotes = false;
ret = format!("$(compgen -W \"{}\" -- ${{cur}})", vals.join(" ")); 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(); let mut it = vec.iter().peekable();
while let Some((_, val)) = it.next() { while let Some((_, val)) = it.next() {
ret = format!("{}<{}>{}", ret, val, ret = format!("{}<{}>{}", ret, val,
@ -321,7 +321,7 @@ fn vals_for(o: &OptBuilder) -> String {
ret ret
} }
fn gen_fish_inner(root_command: &String, fn gen_fish_inner(root_command: &str,
comp_gen: &ComplGen, comp_gen: &ComplGen,
parent_cmds: Vec<&String>, parent_cmds: Vec<&String>,
buffer: &mut String, buffer: &mut String,

View file

@ -66,6 +66,21 @@ OPTIONS:
ARGS: ARGS:
<scpositional> tests positionals"; <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] #[test]
fn help_short() { fn help_short() {
let m = App::new("test") let m = App::new("test")
@ -159,6 +174,21 @@ fn after_and_before_help_output() {
test::check_help(app, AFTER_HELP); 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] #[test]
fn complex_subcommand_help_output() { fn complex_subcommand_help_output() {
let mut a = test::complex_app(); let mut a = test::complex_app();