refactor: moves usage string generation code into it's own module

This commit is contained in:
Kevin K 2017-03-09 16:09:08 -05:00
parent 44ed8b663c
commit 622b609c57
No known key found for this signature in database
GPG key ID: 17218E4B3692F01A
5 changed files with 435 additions and 314 deletions

View file

@ -11,6 +11,7 @@ use app::parser::Parser;
use args::{AnyArg, ArgSettings, DispOrder};
use errors::{Error, Result as ClapResult};
use fmt::{Format, Colorizer};
use app::usage;
// Third Party
use unicode_width::UnicodeWidthStr;
@ -682,7 +683,7 @@ impl<'a> Help<'a> {
try!(write!(self.writer,
"\n{}{}\n\n",
TAB,
parser.create_usage_no_title(&[])));
usage::create_help_usage(parser)));
let flags = parser.has_flags();
let pos = parser.has_positionals();
@ -879,7 +880,7 @@ impl<'a> Help<'a> {
parser.meta.about.unwrap_or("unknown about")));
}
b"usage" => {
try!(write!(self.writer, "{}", parser.create_usage_no_title(&[])));
try!(write!(self.writer, "{}", usage::create_help_usage(parser)));
}
b"all-args" => {
try!(self.write_all_args(&parser));

View file

@ -5,6 +5,7 @@ pub mod parser;
mod meta;
mod help;
mod validator;
mod usage;
// Std
use std::env;

View file

@ -32,6 +32,7 @@ use completions::Shell;
use suggestions;
use app::settings::AppSettings as AS;
use app::validator::Validator;
use app::usage;
#[allow(missing_debug_implementations)]
#[doc(hidden)]
@ -98,7 +99,11 @@ impl<'a, 'b> Parser<'a, 'b>
use std::error::Error;
let out_dir = PathBuf::from(od);
let name = &*self.meta.bin_name.as_ref().unwrap().clone();
let name = &*self.meta
.bin_name
.as_ref()
.unwrap()
.clone();
let file_name = match for_shell {
Shell::Bash => format!("{}.bash-completion", name),
Shell::Fish => format!("{}.fish", name),
@ -249,8 +254,10 @@ impl<'a, 'b> Parser<'a, 'b>
}
}
if self.groups.iter().any(|g| g.name == group.name) {
let grp =
self.groups.iter_mut().find(|g| g.name == group.name).expect(INTERNAL_ERROR_MSG);
let grp = self.groups
.iter_mut()
.find(|g| g.name == group.name)
.expect(INTERNAL_ERROR_MSG);
grp.args.extend_from_slice(&group.args);
grp.requires = group.requires.clone();
grp.conflicts = group.conflicts.clone();
@ -290,7 +297,11 @@ impl<'a, 'b> Parser<'a, 'b>
if vsc {
sc.p.set(AS::DisableVersion);
}
if gv && sc.p.meta.version.is_none() && self.meta.version.is_some() {
if gv &&
sc.p
.meta
.version
.is_none() && self.meta.version.is_some() {
sc.p.set(AS::GlobalVersion);
sc.p.meta.version = Some(self.meta.version.unwrap());
}
@ -306,21 +317,21 @@ impl<'a, 'b> Parser<'a, 'b>
if self.is_set(AS::DeriveDisplayOrder) {
let unified = self.is_set(AS::UnifiedHelpMessage);
for (i, o) in self.opts
.iter_mut()
.enumerate()
.filter(|&(_, ref o)| o.s.disp_ord == 999) {
.iter_mut()
.enumerate()
.filter(|&(_, ref o)| o.s.disp_ord == 999) {
o.s.disp_ord = if unified { o.s.unified_ord } else { i };
}
for (i, f) in self.flags
.iter_mut()
.enumerate()
.filter(|&(_, ref f)| f.s.disp_ord == 999) {
.iter_mut()
.enumerate()
.filter(|&(_, ref f)| f.s.disp_ord == 999) {
f.s.disp_ord = if unified { f.s.unified_ord } else { i };
}
for (i, sc) in &mut self.subcommands
.iter_mut()
.enumerate()
.filter(|&(_, ref sc)| sc.p.meta.disp_ord == 999) {
.iter_mut()
.enumerate()
.filter(|&(_, ref sc)| sc.p.meta.disp_ord == 999) {
sc.p.meta.disp_ord = i;
}
}
@ -441,24 +452,25 @@ impl<'a, 'b> Parser<'a, 'b>
}
}
for a in desc_reqs.iter()
.filter(|name| !self.positionals.values().any(|p| &&p.b.name == name))
.filter(|name| !self.groups.iter().any(|g| &&g.name == name))
.filter(|name| !args_in_groups.contains(name))
.filter(|name| !(matcher.is_some() && matcher.as_ref().unwrap().contains(name))) {
.filter(|name| !self.positionals.values().any(|p| &&p.b.name == name))
.filter(|name| !self.groups.iter().any(|g| &&g.name == name))
.filter(|name| !args_in_groups.contains(name))
.filter(|name| {
!(matcher.is_some() && matcher.as_ref().unwrap().contains(name))
}) {
debugln!("Parser::get_required_from:iter:{}:", a);
let arg = find_by_name!(self, a, flags, iter)
.map(|f| f.to_string())
.unwrap_or_else(|| {
find_by_name!(self, a, opts, iter)
.map(|o| o.to_string())
.expect(INTERNAL_ERROR_MSG)
});
find_by_name!(self, a, opts, iter)
.map(|o| o.to_string())
.expect(INTERNAL_ERROR_MSG)
});
ret_val.push_back(arg);
}
let mut g_vec = vec![];
for g in desc_reqs.iter().filter(|n| self.groups.iter().any(|g| &&g.name == n)) {
let g_string = self.args_in_group(g)
.join("|");
let g_string = self.args_in_group(g).join("|");
g_vec.push(format!("<{}>", &g_string[..g_string.len()]));
}
g_vec.sort();
@ -474,8 +486,10 @@ impl<'a, 'b> Parser<'a, 'b>
pub fn get_args_tag(&self) -> Option<String> {
debugln!("Parser::get_args_tag;");
let mut count = 0;
'outer: for p in self.positionals.values().filter(|p| !p.is_set(ArgSettings::Required) &&
!p.is_set(ArgSettings::Hidden)) {
'outer: for p in self.positionals.values().filter(|p| {
!p.is_set(ArgSettings::Required) &&
!p.is_set(ArgSettings::Hidden)
}) {
debugln!("Parser::get_args_tag:iter:{}:", p.b.name);
if let Some(g_vec) = self.groups_for_arg(p.b.name) {
for grp_s in &g_vec {
@ -487,7 +501,8 @@ impl<'a, 'b> Parser<'a, 'b>
}
}
count += 1;
debugln!("Parser::get_args_tag:iter: {} Args not required or hidden", count);
debugln!("Parser::get_args_tag:iter: {} Args not required or hidden",
count);
}
if !self.is_set(AS::DontCollapseArgsInUsage) && count > 1 {
return None; // [ARGS]
@ -499,12 +514,14 @@ impl<'a, 'b> Parser<'a, 'b>
return Some(format!(" [{}]{}", p.name_no_brackets(), p.multiple_str()));
} else if self.is_set(AS::DontCollapseArgsInUsage) && !self.positionals.is_empty() {
return Some(self.positionals
.values()
.filter(|p| !p.is_set(ArgSettings::Required))
.filter(|p| !p.is_set(ArgSettings::Hidden))
.map(|p| format!(" [{}]{}", p.name_no_brackets(), p.multiple_str()))
.collect::<Vec<_>>()
.join(""));
.values()
.filter(|p| !p.is_set(ArgSettings::Required))
.filter(|p| !p.is_set(ArgSettings::Hidden))
.map(|p| {
format!(" [{}]{}", p.name_no_brackets(), p.multiple_str())
})
.collect::<Vec<_>>()
.join(""));
}
Some("".into())
}
@ -558,26 +575,34 @@ impl<'a, 'b> Parser<'a, 'b>
pub fn has_subcommands(&self) -> bool { !self.subcommands.is_empty() }
#[inline]
pub fn has_visible_opts(&self) -> bool {
if self.opts.is_empty() { return false; }
pub fn has_visible_opts(&self) -> bool {
if self.opts.is_empty() {
return false;
}
self.opts.iter().any(|o| !o.is_set(ArgSettings::Hidden))
}
#[inline]
pub fn has_visible_flags(&self) -> bool {
if self.flags.is_empty() { return false; }
pub fn has_visible_flags(&self) -> bool {
if self.flags.is_empty() {
return false;
}
self.flags.iter().any(|f| !f.is_set(ArgSettings::Hidden))
}
#[inline]
pub fn has_visible_positionals(&self) -> bool {
if self.positionals.is_empty() { return false; }
pub fn has_visible_positionals(&self) -> bool {
if self.positionals.is_empty() {
return false;
}
self.positionals.values().any(|p| !p.is_set(ArgSettings::Hidden))
}
#[inline]
pub fn has_visible_subcommands(&self) -> bool {
if self.subcommands.is_empty() { return false; }
pub fn has_visible_subcommands(&self) -> bool {
if self.subcommands.is_empty() {
return false;
}
self.subcommands.iter().any(|s| !s.p.is_set(AS::Hidden))
}
@ -598,7 +623,10 @@ 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, 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",
@ -608,12 +636,10 @@ impl<'a, 'b> Parser<'a, 'b>
}
// Next we verify that only the highest index has a .multiple(true) (if any)
if
self.positionals
.values()
.any(|a| {
a.is_set(ArgSettings::Multiple) && (a.index as usize != self.positionals.len())
}) {
if self.positionals.values().any(|a| {
a.is_set(ArgSettings::Multiple) &&
(a.index as usize != self.positionals.len())
}) {
debug_assert!({
let mut it = self.positionals.values().rev();
@ -628,7 +654,10 @@ impl<'a, 'b> Parser<'a, 'b>
debug_assert!({
let num = self.positionals.len() - 1;
self.positionals.get(num).unwrap().is_set(ArgSettings::Multiple)
self.positionals
.get(num)
.unwrap()
.is_set(ArgSettings::Multiple)
},
"Only the last positional argument, or second to last positional \
argument may be set to .multiple(true)");
@ -639,8 +668,9 @@ impl<'a, 'b> Parser<'a, 'b>
debug_assert!(self.positionals
.values()
.filter(|p| {
p.b.settings.is_set(ArgSettings::Multiple) && p.v.num_vals.is_none()
})
p.b.settings.is_set(ArgSettings::Multiple) &&
p.v.num_vals.is_none()
})
.map(|_| 1)
.sum::<u64>() <= 1,
"Only one positional argument with .multiple(true) set is allowed per \
@ -729,15 +759,18 @@ impl<'a, 'b> Parser<'a, 'b>
.iter()
.filter(|s| {
starts(&s.p.meta.name[..], &*arg_os) ||
(s.p.meta.aliases.is_some() &&
(s.p
.meta
.aliases
.is_some() &&
s.p
.meta
.aliases
.as_ref()
.unwrap()
.iter()
.filter(|&&(a, _)| starts(a, &*arg_os))
.count() == 1)
.meta
.aliases
.as_ref()
.unwrap()
.iter()
.filter(|&&(a, _)| starts(a, &*arg_os))
.count() == 1)
})
.map(|sc| &sc.p.meta.name)
.collect::<Vec<_>>();
@ -769,24 +802,21 @@ impl<'a, 'b> Parser<'a, 'b>
help_help = true;
}
if let Some(c) = sc.subcommands
.iter()
.find(|s| &*s.p.meta.name == cmd)
.map(|sc| &sc.p) {
.iter()
.find(|s| &*s.p.meta.name == cmd)
.map(|sc| &sc.p) {
sc = c;
if i == cmds.len() - 1 {
break;
}
} else if let Some(c) = sc.subcommands
.iter()
.find(|s| if let Some(ref als) = s.p
.meta
.aliases {
als.iter()
.any(|&(a, _)| &a == &&*cmd.to_string_lossy())
} else {
false
})
.map(|sc| &sc.p) {
.iter()
.find(|s| if let Some(ref als) = s.p.meta.aliases {
als.iter().any(|&(a, _)| &a == &&*cmd.to_string_lossy())
} else {
false
})
.map(|sc| &sc.p) {
sc = c;
if i == cmds.len() - 1 {
break;
@ -957,7 +987,7 @@ impl<'a, 'b> Parser<'a, 'b>
arg_os.to_string_lossy().parse::<f64>().is_ok()) {
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
"",
&*self.create_current_usage(matcher, None),
&*usage::create_error_usage(self, matcher, None),
self.color()));
}
} else if !self.is_set(AS::AllowLeadingHyphen) {
@ -980,7 +1010,7 @@ impl<'a, 'b> Parser<'a, 'b>
.bin_name
.as_ref()
.unwrap_or(&self.meta.name),
&*self.create_current_usage(matcher,
&*usage::create_error_usage(self, matcher,
None),
self.color()));
}
@ -1029,8 +1059,9 @@ impl<'a, 'b> Parser<'a, 'b>
Some(s) => s.to_string(),
None => {
if !self.is_set(AS::StrictUtf8) {
return Err(Error::invalid_utf8(&*self.create_current_usage(matcher,
None),
return Err(Error::invalid_utf8(&*usage::create_error_usage(self,
matcher,
None),
self.color()));
}
arg_os.to_string_lossy().into_owned()
@ -1042,59 +1073,73 @@ impl<'a, 'b> Parser<'a, 'b>
while let Some(v) = it.next() {
let a = v.into();
if a.to_str().is_none() && !self.is_set(AS::StrictUtf8) {
return Err(Error::invalid_utf8(&*self.create_current_usage(matcher, None),
return Err(Error::invalid_utf8(&*usage::create_error_usage(self,
matcher,
None),
self.color()));
}
sc_m.add_val_to("", &a);
}
matcher.subcommand(SubCommand {
name: sc_name,
matches: sc_m.into(),
});
name: sc_name,
matches: sc_m.into(),
});
} else if !(self.is_set(AS::AllowLeadingHyphen) ||
self.is_set(AS::AllowNegativeNumbers)) &&
!self.is_set(AS::InferSubcommands) {
return Err(Error::unknown_argument(&*arg_os.to_string_lossy(),
"",
&*self.create_current_usage(matcher, None),
&*usage::create_error_usage(self,
matcher,
None),
self.color()));
} else if !(has_args) && self.has_subcommands() {
if let Some(cdate) = suggestions::did_you_mean(&*arg_os.to_string_lossy(),
sc_names!(self)) {
return Err(Error::invalid_subcommand(arg_os.to_string_lossy()
.into_owned(),
cdate,
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name),
&*self.create_current_usage(matcher,
None),
self.color()));
return Err(Error::invalid_subcommand(arg_os.to_string_lossy().into_owned(),
cdate,
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name),
&*usage::create_error_usage(self,
matcher,
None),
self.color()));
}
}
}
if let Some(ref pos_sc_name) = subcmd_name {
let sc_name = {
find_subcmd!(self, pos_sc_name).expect(INTERNAL_ERROR_MSG).p.meta.name.clone()
find_subcmd!(self, pos_sc_name)
.expect(INTERNAL_ERROR_MSG)
.p
.meta
.name
.clone()
};
try!(self.parse_subcommand(&*sc_name, matcher, it));
} else if self.is_set(AS::SubcommandRequired) {
let bn = self.meta.bin_name.as_ref().unwrap_or(&self.meta.name);
let bn = self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name);
return Err(Error::missing_subcommand(bn,
&self.create_current_usage(matcher, None),
&usage::create_error_usage(self,
matcher,
None),
self.color()));
} else if self.is_set(AS::SubcommandRequiredElseHelp) {
debugln!("parser::get_matches_with: SubcommandRequiredElseHelp=true");
let mut out = vec![];
try!(self.write_help_err(&mut out));
return Err(Error {
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
}
Validator::new(self).validate(needs_val_of, subcmd_name, matcher)
@ -1113,7 +1158,10 @@ impl<'a, 'b> Parser<'a, 'b>
debugln!("Parser::build_bin_names;");
for sc in &mut self.subcommands {
debug!("Parser::build_bin_names:iter: bin_name set...");
if sc.p.meta.bin_name.is_none() {
if sc.p
.meta
.bin_name
.is_none() {
sdebugln!("No");
let bin_name = format!("{}{}{}",
self.meta
@ -1151,7 +1199,10 @@ impl<'a, 'b> Parser<'a, 'b>
debugln!("Parser::parse_subcommand;");
let mut mid_string = String::new();
if !self.is_set(AS::SubcommandsNegateReqs) {
let mut hs: Vec<&str> = self.required.iter().map(|n| &**n).collect();
let mut hs: Vec<&str> = self.required
.iter()
.map(|n| &**n)
.collect();
for k in matcher.arg_names() {
hs.push(k);
}
@ -1162,14 +1213,15 @@ impl<'a, 'b> Parser<'a, 'b>
}
}
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();
// bin_name should be parent's bin_name + [<reqs>] + the sc's name separated by
// a space
sc.p.meta.usage = Some(format!("{}{}{}",
self.meta.bin_name.as_ref().unwrap_or(&String::new()),
self.meta
.bin_name
.as_ref()
.unwrap_or(&String::new()),
if self.meta.bin_name.is_some() {
&*mid_string
} else {
@ -1192,9 +1244,12 @@ impl<'a, 'b> Parser<'a, 'b>
debugln!("Parser::parse_subcommand: sc settings={:#?}", sc.p.settings);
try!(sc.p.get_matches_with(&mut sc_matcher, it));
matcher.subcommand(SubCommand {
name: sc.p.meta.name.clone(),
matches: sc_matcher.into(),
});
name: sc.p
.meta
.name
.clone(),
matches: sc_matcher.into(),
});
}
Ok(())
}
@ -1228,14 +1283,16 @@ impl<'a, 'b> Parser<'a, 'b>
let mut g_vec = vec![];
let mut args = vec![];
for n in &self.groups.iter().find(|g| g.name == group).expect(INTERNAL_ERROR_MSG).args {
for n in &self.groups
.iter()
.find(|g| g.name == group)
.expect(INTERNAL_ERROR_MSG)
.args {
if let Some(f) = self.flags.iter().find(|f| &f.b.name == n) {
args.push(f.to_string());
} else if let Some(f) = self.opts.iter().find(|o| &o.b.name == n) {
args.push(f.to_string());
} else if let Some(p) = self.positionals
.values()
.find(|p| &p.b.name == n) {
} else if let Some(p) = self.positionals.values().find(|p| &p.b.name == n) {
args.push(p.b.name.to_owned());
} else {
g_vec.push(*n);
@ -1253,7 +1310,11 @@ impl<'a, 'b> Parser<'a, 'b>
let mut g_vec = vec![];
let mut args = vec![];
for n in &self.groups.iter().find(|g| g.name == group).expect(INTERNAL_ERROR_MSG).args {
for n in &self.groups
.iter()
.find(|g| g.name == group)
.expect(INTERNAL_ERROR_MSG)
.args {
if self.groups.iter().any(|g| &g.name == &*n) {
args.extend(self.arg_names_in_group(&*n));
g_vec.push(*n);
@ -1321,26 +1382,6 @@ impl<'a, 'b> Parser<'a, 'b>
// Retrieves the names of all args the user has supplied thus far, except required ones
// because those will be listed in self.required
pub fn create_current_usage(&self, matcher: &'b ArgMatcher<'a>, extra: Option<&str>) -> String {
let mut args: Vec<_> = matcher.arg_names()
.iter()
.filter(|n| {
if let Some(o) = find_by_name!(self, *n, opts, iter) {
!o.b.settings.is_set(ArgSettings::Required)
} else if let Some(p) = find_by_name!(self, *n, positionals, values) {
!p.b.settings.is_set(ArgSettings::Required)
} else {
true // flags can't be required, so they're always true
}
})
.map(|&n| n)
.collect();
if let Some(r) = extra {
args.push(r);
}
self.create_usage(&*args)
}
fn check_for_help_and_version_str(&self, arg: &OsStr) -> ClapResult<()> {
debugln!("Parser::check_for_help_and_version_str;");
debug!("Parser::check_for_help_and_version_str: Checking if --{} is help or version...",
@ -1382,10 +1423,10 @@ impl<'a, 'b> Parser<'a, 'b>
let mut buf = vec![];
try!(Help::write_parser_help(&mut buf, self));
Err(Error {
message: unsafe { String::from_utf8_unchecked(buf) },
kind: ErrorKind::HelpDisplayed,
info: None,
})
message: unsafe { String::from_utf8_unchecked(buf) },
kind: ErrorKind::HelpDisplayed,
info: None,
})
}
fn _version(&self) -> ClapResult<()> {
@ -1393,10 +1434,10 @@ impl<'a, 'b> Parser<'a, 'b>
let mut buf_w = BufWriter::new(out.lock());
try!(self.print_version(&mut buf_w));
Err(Error {
message: String::new(),
kind: ErrorKind::VersionDisplayed,
info: None,
})
message: String::new(),
kind: ErrorKind::VersionDisplayed,
info: None,
})
}
fn parse_long_arg(&mut self,
@ -1530,7 +1571,9 @@ impl<'a, 'b> Parser<'a, 'b>
let arg = format!("-{}", c);
return Err(Error::unknown_argument(&*arg,
"",
&*self.create_current_usage(matcher, None),
&*usage::create_error_usage(self,
matcher,
None),
self.color()));
}
}
@ -1555,7 +1598,7 @@ impl<'a, 'b> Parser<'a, 'b>
(v.len_() == 0 || (opt.is_set(ArgSettings::RequireEquals) && !has_eq)) {
sdebugln!("Found Empty - Error");
return Err(Error::empty_value(opt,
&*self.create_current_usage(matcher, None),
&*usage::create_error_usage(self, matcher, None),
self.color()));
}
sdebugln!("Found - {:?}, len: {}", v, v.len_());
@ -1566,7 +1609,7 @@ impl<'a, 'b> Parser<'a, 'b>
} else if opt.is_set(ArgSettings::RequireEquals) && !opt.is_set(ArgSettings::EmptyValues) {
sdebugln!("None, but requires equals...Error");
return Err(Error::empty_value(opt,
&*self.create_current_usage(matcher, None),
&*usage::create_error_usage(self, matcher, None),
self.color()));
} else {
@ -1689,130 +1732,10 @@ impl<'a, 'b> Parser<'a, 'b>
let used_arg = format!("--{}", arg);
Err(Error::unknown_argument(&*used_arg,
&*suffix.0,
&*self.create_current_usage(matcher, None),
&*usage::create_error_usage(self, matcher, None),
self.color()))
}
// Creates a usage string if one was not provided by the user manually. This happens just
// after all arguments were parsed, but before any subcommands have been parsed
// (so as to give subcommands their own usage recursively)
pub fn create_usage(&self, used: &[&str]) -> String {
debugln!("Parser::create_usage;");
let mut usage = String::with_capacity(75);
usage.push_str("USAGE:\n ");
usage.push_str(&self.create_usage_no_title(used));
usage
}
// Creates a usage string (*without title*) if one was not provided by the user
// manually. This happens just
// after all arguments were parsed, but before any subcommands have been parsed
// (so as to give subcommands their own usage recursively)
pub fn create_usage_no_title(&self, used: &[&str]) -> String {
debugln!("Parser::create_usage_no_title;");
let mut usage = String::with_capacity(75);
if let Some(u) = self.meta.usage_str {
usage.push_str(&*u);
} else if used.is_empty() {
let name = self.meta
.usage
.as_ref()
.unwrap_or_else(|| {
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta.name)
});
usage.push_str(&*name);
let mut reqs: Vec<&str> = self.required().map(|r| &**r).collect();
reqs.dedup();
let req_string = self.get_required_from(&reqs, None, None)
.iter()
.fold(String::new(), |a, s| a + &format!(" {}", s)[..]);
let flags = self.needs_flags_tag();
if flags && !self.is_set(AS::UnifiedHelpMessage) {
usage.push_str(" [FLAGS]");
} else if flags {
usage.push_str(" [OPTIONS]");
}
if !self.is_set(AS::UnifiedHelpMessage) &&
self.opts.iter().any(|o| !o.is_set(ArgSettings::Required) &&
!o.is_set(ArgSettings::Hidden)) {
usage.push_str(" [OPTIONS]");
}
usage.push_str(&req_string[..]);
// places a '--' in the usage string if there are args and options
// supporting multiple values
if self.has_positionals() &&
self.opts.iter().any(|o| o.is_set(ArgSettings::Multiple)) &&
self.positionals.values().any(|p| !p.is_set(ArgSettings::Required)) &&
!self.has_visible_subcommands() {
usage.push_str(" [--]")
}
if self.has_positionals() &&
self.positionals.values().any(|p| !p.is_set(ArgSettings::Required) &&
!p.is_set(ArgSettings::Hidden)) {
if let Some(args_tag) = self.get_args_tag() {
usage.push_str(&*args_tag);
} else {
usage.push_str(" [ARGS]");
}
}
if self.is_set(AS::SubcommandsNegateReqs) || self.is_set(AS::ArgsNegateSubcommands) {
if self.has_visible_subcommands() {
usage.push_str("\n ");
usage.push_str(&*name);
usage.push_str(" <SUBCOMMAND>");
}
} else {
if self.has_visible_subcommands() && !self.is_set(AS::SubcommandRequired) {
usage.push_str(" [SUBCOMMAND]");
} else if (self.is_set(AS::SubcommandRequired) ||
self.is_set(AS::SubcommandRequiredElseHelp)) && self.has_subcommands() {
usage.push_str(" <SUBCOMMAND>");
}
}
} else {
self.smart_usage(&mut usage, used);
}
usage.shrink_to_fit();
usage
}
// Creates a context aware usage string, or "smart usage" from currently used
// args, and requirements
fn smart_usage(&self, usage: &mut String, used: &[&str]) {
debugln!("Parser::smart_usage;");
let mut hs: Vec<&str> = self.required().map(|s| &**s).collect();
hs.extend_from_slice(used);
let r_string = self.get_required_from(&hs, None, None)
.iter()
.fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]);
usage.push_str(&self.meta
.usage
.as_ref()
.unwrap_or_else(|| {
self.meta
.bin_name
.as_ref()
.unwrap_or(&self.meta
.name)
})
[..]);
usage.push_str(&*r_string);
if self.is_set(AS::SubcommandRequired) {
usage.push_str(" <SUBCOMMAND>");
}
}
// Prints the version to the user and exits if quit=true
fn print_version<W: Write>(&self, w: &mut W) -> ClapResult<()> {
try!(self.write_version(w));
@ -1955,17 +1878,33 @@ impl<'a, 'b> Parser<'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: Currently in Parser...{}",
self.meta.bin_name.as_ref().unwrap());
self.meta
.bin_name
.as_ref()
.unwrap());
for s in self.subcommands.iter() {
if s.p.meta.bin_name.as_ref().unwrap_or(&String::new()) == sc ||
(s.p.meta.aliases.is_some() &&
if s.p
.meta
.bin_name
.as_ref()
.unwrap_or(&String::new()) == sc ||
(s.p
.meta
.aliases
.is_some() &&
s.p
.meta
.aliases
.as_ref()
.unwrap()
.iter()
.any(|&(s, _)| s == sc.split(' ').rev().next().expect(INTERNAL_ERROR_MSG))) {
.meta
.aliases
.as_ref()
.unwrap()
.iter()
.any(|&(s, _)| {
s ==
sc.split(' ')
.rev()
.next()
.expect(INTERNAL_ERROR_MSG)
})) {
return Some(s);
}
if let Some(app) = s.p.find_subcommand(sc) {

153
src/app/usage.rs Normal file
View file

@ -0,0 +1,153 @@
use args::{AnyArg, ArgMatcher};
use args::settings::ArgSettings;
use app::settings::AppSettings as AS;
use app::parser::Parser;
// Creates a usage string for display. This happens just after all arguments were parsed, but before
// any subcommands have been parsed (so as to give subcommands their own usage recursively)
pub fn create_usage_with_title(p: &Parser, used: &[&str]) -> String {
debugln!("Parser::create_usage_with_title;");
let mut usage = String::with_capacity(75);
usage.push_str("USAGE:\n ");
usage.push_str(&*create_usage_no_title(p, used));
usage
}
// Creates a usage string to be used in error message (i.e. one with currently used args)
pub fn create_error_usage<'a, 'b>(p: &Parser<'a, 'b>,
matcher: &'b ArgMatcher<'a>,
extra: Option<&str>)
-> String {
let mut args: Vec<_> = matcher.arg_names()
.iter()
.filter(|n| {
if let Some(o) = find_by_name!(p, *n, opts, iter) {
!o.b.is_set(ArgSettings::Required) && !o.b.is_set(ArgSettings::Hidden)
} else if let Some(p) = find_by_name!(p, *n, positionals, values) {
!p.b.is_set(ArgSettings::Required) && p.b.is_set(ArgSettings::Hidden)
} else {
true // flags can't be required, so they're always true
}
})
.map(|&n| n)
.collect();
if let Some(r) = extra {
args.push(r);
}
create_usage_with_title(p, &*args)
}
// Creates a usage string (*without title*) if one was not provided by the user manually.
fn create_usage_no_title(p: &Parser, used: &[&str]) -> String {
debugln!("Parser::create_usage_no_title;");
if let Some(u) = p.meta.usage_str {
String::from(&*u)
} else if used.is_empty() {
create_help_usage(p)
} else {
create_smart_usage(p, used)
}
}
// Creates a usage string for display in help messages (i.e. not for errors)
pub fn create_help_usage(p: &Parser) -> String {
let mut usage = String::with_capacity(75);
let name = p.meta
.usage
.as_ref()
.unwrap_or_else(|| {
p.meta
.bin_name
.as_ref()
.unwrap_or(&p.meta.name)
});
usage.push_str(&*name);
let mut reqs: Vec<&str> = p.required().map(|r| &**r).collect();
reqs.dedup();
let req_string =
p.get_required_from(&reqs, None, None).iter().fold(String::new(),
|a, s| a + &format!(" {}", s)[..]);
let flags = p.needs_flags_tag();
if flags && !p.is_set(AS::UnifiedHelpMessage) {
usage.push_str(" [FLAGS]");
} else if flags {
usage.push_str(" [OPTIONS]");
}
if !p.is_set(AS::UnifiedHelpMessage) &&
p.opts.iter().any(|o| {
!o.is_set(ArgSettings::Required) && !o.is_set(ArgSettings::Hidden)
}) {
usage.push_str(" [OPTIONS]");
}
usage.push_str(&req_string[..]);
// places a '--' in the usage string if there are args and options
// supporting multiple values
if p.has_positionals() && p.opts.iter().any(|o| o.is_set(ArgSettings::Multiple)) &&
p.positionals.values().any(|p| !p.is_set(ArgSettings::Required)) &&
!p.has_visible_subcommands() {
usage.push_str(" [--]")
}
if p.has_positionals() &&
p.positionals.values().any(|p| {
!p.is_set(ArgSettings::Required) &&
!p.is_set(ArgSettings::Hidden)
}) {
if let Some(args_tag) = p.get_args_tag() {
usage.push_str(&*args_tag);
} else {
usage.push_str(" [ARGS]");
}
}
if p.is_set(AS::SubcommandsNegateReqs) || p.is_set(AS::ArgsNegateSubcommands) {
if p.has_visible_subcommands() {
usage.push_str("\n ");
usage.push_str(&*name);
usage.push_str(" <SUBCOMMAND>");
}
} else {
if p.has_visible_subcommands() && !p.is_set(AS::SubcommandRequired) {
usage.push_str(" [SUBCOMMAND]");
} else if (p.is_set(AS::SubcommandRequired) ||
p.is_set(AS::SubcommandRequiredElseHelp)) &&
p.has_subcommands() {
usage.push_str(" <SUBCOMMAND>");
}
}
usage.shrink_to_fit();
usage
}
// Creates a context aware usage string, or "smart usage" from currently used
// args, and requirements
fn create_smart_usage(p: &Parser, used: &[&str]) -> String {
debugln!("Parser::smart_usage;");
let mut usage = String::with_capacity(75);
let mut hs: Vec<&str> = p.required().map(|s| &**s).collect();
hs.extend_from_slice(used);
let r_string = p.get_required_from(&hs, None, None).iter().fold(String::new(), |acc, s| {
acc + &format!(" {}", s)[..]
});
usage.push_str(&p.meta
.usage
.as_ref()
.unwrap_or_else(|| {
p.meta
.bin_name
.as_ref()
.unwrap_or(&p.meta.name)
})
[..]);
usage.push_str(&*r_string);
if p.is_set(AS::SubcommandRequired) {
usage.push_str(" <SUBCOMMAND>");
}
usage.shrink_to_fit();
usage
}

View file

@ -12,19 +12,20 @@ use osstringext::OsStrExt2;
use app::settings::AppSettings as AS;
use app::parser::Parser;
use fmt::Colorizer;
use app::usage;
pub struct Validator<'a, 'b, 'z>(&'z mut Parser<'a, 'b>) where 'a: 'b, 'b: 'z;
pub struct Validator<'a, 'b, 'z>(&'z mut Parser<'a, 'b>)
where 'a: 'b,
'b: 'z;
impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
pub fn new(p: &'z mut Parser<'a, 'b>) -> Self {
Validator(p)
}
pub fn new(p: &'z mut Parser<'a, 'b>) -> Self { Validator(p) }
pub fn validate(&mut self,
needs_val_of: Option<&'a str>,
subcmd_name: Option<String>,
matcher: &mut ArgMatcher<'a>)
-> ClapResult<()> {
needs_val_of: Option<&'a str>,
subcmd_name: Option<String>,
matcher: &mut ArgMatcher<'a>)
-> ClapResult<()> {
debugln!("Validator::validate;");
let mut reqs_validated = false;
try!(self.0.add_defaults(matcher));
@ -40,7 +41,9 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
};
if should_err {
return Err(Error::empty_value(o,
&*self.0.create_current_usage(matcher, None),
&*usage::create_error_usage(self.0,
matcher,
None),
self.0.color()));
}
}
@ -51,17 +54,17 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
try!(self.validate_required(matcher));
}
try!(self.validate_matched_args(matcher));
matcher.usage(self.0.create_usage(&[]));
matcher.usage(usage::create_help_usage(self.0));
if matcher.is_empty() && matcher.subcommand_name().is_none() &&
self.0.is_set(AS::ArgRequiredElseHelp) {
let mut out = vec![];
try!(self.0.write_help_err(&mut out));
return Err(Error {
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
}
Ok(())
}
@ -78,7 +81,9 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
if self.0.is_set(AS::StrictUtf8) && val.to_str().is_none() {
debugln!("Validator::validate_values: invalid UTF-8 found in val {:?}",
val);
return Err(Error::invalid_utf8(&*self.0.create_current_usage(matcher, None),
return Err(Error::invalid_utf8(&*usage::create_error_usage(self.0,
matcher,
None),
self.0.color()));
}
if let Some(p_vals) = arg.possible_vals() {
@ -88,7 +93,9 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
return Err(Error::invalid_value(val_str,
p_vals,
arg,
&*self.0.create_current_usage(matcher, None),
&*usage::create_error_usage(self.0,
matcher,
None),
self.0.color()));
}
}
@ -96,7 +103,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
matcher.contains(&*arg.name()) {
debugln!("Validator::validate_values: illegal empty val found");
return Err(Error::empty_value(arg,
&*self.0.create_current_usage(matcher, None),
&*usage::create_error_usage(self.0, matcher, None),
self.0.color()));
}
if let Some(vtor) = arg.validator() {
@ -124,7 +131,8 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
}
fn validate_blacklist(&self, matcher: &mut ArgMatcher) -> ClapResult<()> {
debugln!("Validator::validate_blacklist: blacklist={:?}", self.0.blacklist);
debugln!("Validator::validate_blacklist: blacklist={:?}",
self.0.blacklist);
macro_rules! build_err {
($p:expr, $name:expr, $matcher:ident) => ({
debugln!("build_err!: name={}", $name);
@ -138,7 +146,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
);
debugln!("build_err!: '{:?}' conflicts with '{}'", c_with, $name);
$matcher.remove($name);
let usg = $p.create_current_usage($matcher, None);
let usg = usage::create_error_usage($p, $matcher, None);
if let Some(f) = find_by_name!($p, $name, flags, iter) {
debugln!("build_err!: It was a flag...");
Error::argument_conflict(f, c_with, &*usg, self.0.color())
@ -160,7 +168,10 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
for name in &self.0.blacklist {
debugln!("Validator::validate_blacklist:iter: Checking blacklisted name: {}",
name);
if self.0.groups.iter().any(|g| &g.name == name) {
if self.0
.groups
.iter()
.any(|g| &g.name == name) {
debugln!("Validator::validate_blacklist:iter: groups contains it...");
for n in self.0.arg_names_in_group(name) {
debugln!("Validator::validate_blacklist:iter:iter: Checking arg '{}' in group...",
@ -198,7 +209,11 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
try!(self.validate_values(pos, ma, matcher));
try!(self.validate_arg_requires(pos, ma, matcher));
} else {
let grp = self.0.groups.iter().find(|g| &g.name == name).expect(INTERNAL_ERROR_MSG);
let grp = self.0
.groups
.iter()
.find(|g| &g.name == name)
.expect(INTERNAL_ERROR_MSG);
if let Some(ref g_reqs) = grp.requires {
if g_reqs.iter().any(|&n| !matcher.contains(n)) {
return self.missing_required_error(matcher, None);
@ -220,7 +235,9 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
if ma.occurs > 1 && !a.is_set(ArgSettings::Multiple) {
// Not the first time, and we don't allow multiples
return Err(Error::unexpected_multiple_usage(a,
&*self.0.create_current_usage(matcher, None),
&*usage::create_error_usage(self.0,
matcher,
None),
self.0.color()));
}
Ok(())
@ -258,7 +275,9 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
} else {
"ere"
},
&*self.0.create_current_usage(matcher, None),
&*usage::create_error_usage(self.0,
matcher,
None),
self.0.color()));
}
}
@ -273,7 +292,7 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
.to_str()
.expect(INVALID_UTF8),
a,
&*self.0.create_current_usage(matcher, None),
&*usage::create_error_usage(self.0, matcher, None),
self.0.color()));
}
}
@ -284,14 +303,14 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
return Err(Error::too_few_values(a,
num,
ma.vals.len(),
&*self.0.create_current_usage(matcher, None),
&*usage::create_error_usage(self.0, matcher, None),
self.0.color()));
}
}
// Issue 665 (https://github.com/kbknapp/clap-rs/issues/665)
if a.takes_value() && !a.is_set(ArgSettings::EmptyValues) && ma.vals.is_empty() {
return Err(Error::empty_value(a,
&*self.0.create_current_usage(matcher, None),
&*usage::create_error_usage(self.0, matcher, None),
self.0.color()));
}
Ok(())
@ -307,9 +326,10 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
debugln!("Validator::validate_arg_requires;");
if let Some(a_reqs) = a.requires() {
for &(val, name) in a_reqs.iter().filter(|&&(val, _)| val.is_some()) {
if ma.vals
.iter()
.any(|v| v == val.expect(INTERNAL_ERROR_MSG) && !matcher.contains(name)) {
if ma.vals.iter().any(|v| {
v == val.expect(INTERNAL_ERROR_MSG) &&
!matcher.contains(name)
}) {
return self.missing_required_error(matcher, None);
}
}
@ -318,7 +338,8 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
}
fn validate_required(&self, matcher: &ArgMatcher) -> ClapResult<()> {
debugln!("Validator::validate_required: required={:?};", self.0.required);
debugln!("Validator::validate_required: required={:?};",
self.0.required);
'outer: for name in &self.0.required {
debugln!("Validator::validate_required:iter:{}:", name);
if matcher.contains(name) {
@ -360,7 +381,8 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
a.blacklist().map(|bl| {
bl.iter().any(|conf| {
matcher.contains(conf) ||
self.0.groups
self.0
.groups
.iter()
.find(|g| &g.name == conf)
.map_or(false, |g| g.args.iter().any(|arg| matcher.contains(arg)))
@ -401,21 +423,26 @@ impl<'a, 'b, 'z> Validator<'a, 'b, 'z> {
use_stderr: true,
when: self.0.color(),
};
let mut reqs = self.0.required.iter().map(|&r| &*r).collect::<Vec<_>>();
let mut reqs = self.0
.required
.iter()
.map(|&r| &*r)
.collect::<Vec<_>>();
if let Some(r) = extra {
reqs.push(r);
}
reqs.retain(|n| !matcher.contains(n));
reqs.dedup();
debugln!("Validator::missing_required_error: reqs={:#?}", reqs);
Err(Error::missing_required_argument(&*self.0.get_required_from(&reqs[..],
Some(matcher),
extra)
.iter()
.fold(String::new(), |acc, s| {
acc + &format!("\n {}", c.error(s))[..]
}),
&*self.0.create_current_usage(matcher, extra),
Err(Error::missing_required_argument(&*self.0
.get_required_from(&reqs[..],
Some(matcher),
extra)
.iter()
.fold(String::new(), |acc, s| {
acc + &format!("\n {}", c.error(s))[..]
}),
&*usage::create_error_usage(self.0, matcher, extra),
self.0.color()))
}