fix(Requirements): fixing requirements and conflicts for issue 1158

Fixes requirements and conflicts on the v3 branch

Closes #1158
This commit is contained in:
Kevin K 2018-07-25 14:46:28 -04:00
parent c22bc3e1fc
commit 5a06a8270a
No known key found for this signature in database
GPG key ID: 2E39D46AABC94DDD
11 changed files with 761 additions and 547 deletions

View file

@ -10,6 +10,7 @@ use std::io::{self, BufRead, BufWriter, Write};
use std::iter::Peekable;
use std::path::{Path, PathBuf};
use std::process;
use std::slice::Iter;
// Third Party
#[cfg(feature = "yaml")]
@ -22,6 +23,7 @@ use output::fmt::ColorWhen;
use output::{Help, Usage};
use parse::errors::Result as ClapResult;
use parse::{ArgMatcher, ArgMatches, Parser};
use INTERNAL_ERROR_MSG;
#[doc(hidden)]
#[allow(dead_code)]
@ -1403,9 +1405,7 @@ impl<'a, 'b> App<'a, 'b> {
let mut parser = Parser::new(self);
// do the real parsing
if let Err(e) = parser.get_matches_with(&mut matcher, it) {
return Err(e);
}
parser.get_matches_with(&mut matcher, it)?;
}
let global_arg_vec: Vec<&str> = (&self)
@ -1449,7 +1449,7 @@ impl<'a, 'b> App<'a, 'b> {
if let Some(ref grps) = a.groups {
for g in grps {
let mut found = false;
if let Some(ref mut ag) = groups_mut!(self).find(|grp| &grp.name == g) {
if let Some(ref mut ag) = self.groups.iter_mut().find(|grp| &grp.name == g) {
ag.args.push(a.name);
found = true;
}
@ -1480,10 +1480,10 @@ impl<'a, 'b> App<'a, 'b> {
debugln!("App::app_debug_asserts;");
// * Args listed inside groups should exist
// * Groups should not have naming conflicts with Args
let g = groups!(self).find(|g| {
g.args
.iter()
.any(|arg| !(find!(self, arg).is_some() || groups!(self).any(|g| &g.name == arg)))
let g = self.groups.iter().find(|g| {
g.args.iter().any(|arg| {
!(self.find(arg).is_some() || self.groups.iter().any(|g| &g.name == arg))
})
});
assert!(
g.is_none(),
@ -1725,6 +1725,10 @@ impl<'a, 'b> App<'a, 'b> {
// Internal Query Methods
#[doc(hidden)]
impl<'a, 'b> App<'a, 'b> {
pub(crate) fn find(&self, name: &str) -> Option<&Arg<'a, 'b>> {
self.args.iter().find(|a| a.name == name)
}
// Should we color the output? None=determined by output location, true=yes, false=no
#[doc(hidden)]
pub fn color(&self) -> ColorWhen {
@ -1780,6 +1784,101 @@ impl<'a, 'b> App<'a, 'b> {
.filter(|sc| sc.name != "help")
.any(|sc| !sc.is_set(AppSettings::Hidden))
}
pub(crate) fn unroll_args_in_group(&self, group: &'a str) -> Vec<&'a str> {
let mut g_vec = vec![group];
let mut args = vec![];
while let Some(ref g) = g_vec.pop() {
for n in self
.groups
.iter()
.find(|grp| &grp.name == g)
.expect(INTERNAL_ERROR_MSG)
.args
.iter()
{
if !args.contains(n) {
if self.find(n).is_some() {
args.push(n)
} else {
g_vec.push(n);
}
}
}
}
args
}
pub(crate) fn unroll_conflicts_for_group(&self, group: &'a str) -> Vec<&'a str> {
let mut g_vec = vec![group];
let mut confs = vec![];
while let Some(ref g) = g_vec.pop() {
if let Some(ref c_vec) = self
.groups
.iter()
.find(|grp| &grp.name == g)
.expect(INTERNAL_ERROR_MSG)
.conflicts
{
for c in c_vec {
if !confs.contains(c) {
if self.find(c).is_some() {
confs.push(c)
} else {
g_vec.push(c);
}
}
}
}
}
confs
}
pub(crate) fn unroll_requirements_for_arg(
&self,
arg: &str,
matcher: &ArgMatcher<'a>,
) -> Vec<&'a str> {
let requires_if_or_not = |&(val, req_arg)| {
if let Some(v) = val {
if matcher
.get(arg)
.and_then(|ma| Some(ma.contains_val(v)))
.unwrap_or(false)
{
Some(req_arg)
} else {
None
}
} else {
Some(req_arg)
}
};
let mut r_vec = vec![arg];
let mut args = vec![];
while let Some(ref a) = r_vec.pop() {
if let Some(arg) = self.find(a) {
if let Some(ref reqs) = arg.requires {
for r in reqs.iter().filter_map(requires_if_or_not) {
if let Some(req) = self.find(r) {
if req.requires.is_some() {
r_vec.push(req.name)
}
}
args.push(r);
}
}
}
}
args
}
}
// @TODO @v3-beta: remove

View file

@ -603,7 +603,7 @@ impl<'a, 'b> Arg<'a, 'b> {
} else {
self.r_unless = Some(vec![name]);
}
self.required(true)
self
}
/// Sets args that override this arg's required setting. (i.e. this arg will be required unless
@ -677,8 +677,7 @@ impl<'a, 'b> Arg<'a, 'b> {
} else {
self.r_unless = Some(names.iter().map(|s| *s).collect::<Vec<_>>());
}
self.setb(ArgSettings::RequiredUnlessAll);
self.required(true)
self.setting(ArgSettings::RequiredUnlessAll)
}
/// Sets args that override this arg's [required] setting. (i.e. this arg will be required
@ -753,7 +752,7 @@ impl<'a, 'b> Arg<'a, 'b> {
} else {
self.r_unless = Some(names.iter().map(|s| *s).collect::<Vec<_>>());
}
self.required(true)
self
}
/// Sets a conflicting argument by name. I.e. when using this argument,

View file

@ -983,20 +983,30 @@ macro_rules! subcommands_mut {
}
}
macro_rules! groups {
($app:expr, $how:ident) => {
$app.groups.$how()
};
($app:expr) => {
groups!($app, iter)
}
// macro_rules! groups {
// ($app:expr, $how:ident) => {
// $app.groups.$how()
// };
// ($app:expr) => {
// groups!($app, iter)
// }
// }
// macro_rules! groups_mut {
// ($app:expr) => {
// groups!($app, iter_mut)
// }
// }
macro_rules! groups_for_arg {
($app:expr, $grp:expr) => {{
debugln!("Parser::groups_for_arg: name={}", $grp);
$app.groups
.iter()
.filter(|grp| grp.args.iter().any(|a| a == $grp))
.map(|grp| grp.name)
}}
}
macro_rules! groups_mut {
($app:expr) => {
groups!($app, iter_mut)
}
}
macro_rules! find_from {
($app:expr, $arg_name:expr, $from:ident, $matcher:expr) => {{

View file

@ -7,18 +7,18 @@ use std::usize;
// Internal
use build::{App, AppSettings, Arg, ArgSettings};
use parse::Parser;
use parse::errors::{Error, Result as ClapResult};
use output::fmt::{Colorizer, ColorizerOption, Format};
use output::Usage;
use parse::errors::{Error, Result as ClapResult};
use parse::Parser;
use util::VecMap;
use INTERNAL_ERROR_MSG;
// Third Party
use unicode_width::UnicodeWidthStr;
#[cfg(feature = "wrap_help")]
use term_size;
use textwrap;
use unicode_width::UnicodeWidthStr;
#[cfg(not(feature = "wrap_help"))]
mod term_size {
@ -173,18 +173,16 @@ impl<'w> Help<'w> {
impl<'w> Help<'w> {
/// Writes help for each argument in the order they were declared to the wrapped stream.
fn write_args_unsorted<'a, 'b, I>(&mut self, args: I) -> io::Result<()>
where
'a: 'b,
I: Iterator<Item=&'b Arg<'a, 'b>>,
where
'a: 'b,
I: Iterator<Item = &'b Arg<'a, 'b>>,
{
debugln!("Help::write_args_unsorted;");
// The shortest an arg can legally be is 2 (i.e. '-x')
self.longest = 2;
let mut arg_v = Vec::with_capacity(10);
let use_long = self.use_long;
for arg in args.filter(|arg| {
should_show_arg(use_long, *arg)
}) {
for arg in args.filter(|arg| should_show_arg(use_long, *arg)) {
if arg.longest_filter() {
self.longest = cmp::max(self.longest, str_width(arg.to_string().as_str()));
}
@ -207,9 +205,9 @@ impl<'w> Help<'w> {
/// Sorts arguments by length and display order and write their help to the wrapped stream.
fn write_args<'a, 'b, I>(&mut self, args: I) -> io::Result<()>
where
'a: 'b,
I: Iterator<Item=&'b Arg<'a, 'b>>,
where
'a: 'b,
I: Iterator<Item = &'b Arg<'a, 'b>>,
{
debugln!("Help::write_args;");
// The shortest an arg can legally be is 2 (i.e. '-x')
@ -346,7 +344,8 @@ impl<'w> Help<'w> {
let h_w = str_width(h) + str_width(&*spec_vals);
let nlh = self.next_line_help || arg.is_set(ArgSettings::NextLineHelp);
let taken = self.longest + 12;
self.force_next_line = !nlh && self.term_w >= taken
self.force_next_line = !nlh
&& self.term_w >= taken
&& (taken as f32 / self.term_w as f32) > 0.40
&& h_w > (self.term_w - taken);
@ -427,7 +426,12 @@ impl<'w> Help<'w> {
}
/// Writes argument's help to the wrapped stream.
fn help<'b, 'c>(&mut self, arg: &Arg<'b, 'c>, spec_vals: &str, prevent_nlh: bool) -> io::Result<()> {
fn help<'b, 'c>(
&mut self,
arg: &Arg<'b, 'c>,
spec_vals: &str,
prevent_nlh: bool,
) -> io::Result<()> {
debugln!("Help::help;");
let h = if self.use_long {
arg.long_help.unwrap_or_else(|| arg.help.unwrap_or(""))
@ -577,7 +581,8 @@ impl<'w> Help<'w> {
let h_w = str_width(h) + str_width(&*spec_vals);
let nlh = self.next_line_help;
let taken = self.longest + 12;
self.force_next_line = !nlh && self.term_w >= taken
self.force_next_line = !nlh
&& self.term_w >= taken
&& (taken as f32 / self.term_w as f32) > 0.40
&& h_w > (self.term_w - taken);
@ -739,16 +744,22 @@ impl<'w> Help<'w> {
first = false;
}
if custom_headings {
for heading in parser.app.help_headings.iter()
for heading in parser
.app
.help_headings
.iter()
.filter(|heading| heading.is_some())
.map(|heading| heading.unwrap()) {
if !first {
self.writer.write_all(b"\n\n")?;
}
color!(self, format!("{}:\n", heading), warning)?;
self.write_args(custom_headings!(parser.app).filter(|a| a.help_heading.unwrap() == heading))?;
first = false
.map(|heading| heading.unwrap())
{
if !first {
self.writer.write_all(b"\n\n")?;
}
color!(self, format!("{}:\n", heading), warning)?;
self.write_args(
custom_headings!(parser.app).filter(|a| a.help_heading.unwrap() == heading),
)?;
first = false
}
}
}
@ -832,8 +843,7 @@ impl<'w> Help<'w> {
($thing:expr) => {{
let mut owned_thing = $thing.to_owned();
owned_thing = owned_thing.replace("{n}", "\n");
write!(self.writer, "{}\n",
wrap_help(&owned_thing, self.term_w))?
write!(self.writer, "{}\n", wrap_help(&owned_thing, self.term_w))?
}};
}
// Print the version
@ -1113,16 +1123,19 @@ impl<'w> Help<'w> {
}
fn should_show_arg(use_long: bool, arg: &Arg) -> bool {
debugln!("Help::should_show_arg: use_long={:?}, arg={}", use_long, arg.name);
debugln!(
"Help::should_show_arg: use_long={:?}, arg={}",
use_long,
arg.name
);
if arg.is_set(ArgSettings::Hidden) {
return false;
}
(!arg.is_set(ArgSettings::HiddenLongHelp) && use_long) ||
(!arg.is_set(ArgSettings::HiddenShortHelp) && !use_long) ||
arg.is_set(ArgSettings::NextLineHelp)
(!arg.is_set(ArgSettings::HiddenLongHelp) && use_long)
|| (!arg.is_set(ArgSettings::HiddenShortHelp) && !use_long)
|| arg.is_set(ArgSettings::NextLineHelp)
}
fn wrap_help(help: &str, avail_chars: usize) -> String {
let wrapper = textwrap::Wrapper::new(avail_chars).break_words(false);
help.lines()

View file

@ -7,14 +7,17 @@ use build::{Arg, ArgSettings};
use parse::{ArgMatcher, Parser};
use INTERNAL_ERROR_MSG;
pub struct Usage<'a, 'b, 'c, 'z>(&'z Parser<'a, 'b, 'c>)
pub struct Usage<'a, 'b, 'c, 'z>
where
'a: 'b,
'b: 'c,
'c: 'z;
'c: 'z,
{
p: &'z Parser<'a, 'b, 'c>,
}
impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
pub fn new(p: &'z Parser<'a, 'b, 'c>) -> Self { Usage(p) }
pub fn new(p: &'z Parser<'a, 'b, 'c>) -> Self { Usage { p: p } }
// 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)
@ -26,29 +29,10 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
usage
}
// Creates a usage string to be used in error message (i.e. one with currently used args)
pub fn create_error_usage(&self, matcher: &ArgMatcher<'a>, extra: Option<&str>) -> String {
let mut args: Vec<_> = matcher
.arg_names()
.filter(|ref n| {
if let Some(a) = find!(self.0.app, **n) {
!a.is_set(ArgSettings::Required) && !a.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);
}
self.create_usage_with_title(&*args)
}
// Creates a usage string (*without title*) if one was not provided by the user manually.
pub fn create_usage_no_title(&self, used: &[&str]) -> String {
debugln!("usage::create_usage_no_title;");
if let Some(u) = self.0.app.usage_str {
if let Some(u) = self.p.app.usage_str {
String::from(&*u)
} else if used.is_empty() {
self.create_help_usage(true)
@ -62,17 +46,14 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
debugln!("Usage::create_help_usage; incl_reqs={:?}", incl_reqs);
let mut usage = String::with_capacity(75);
let name = self
.0
.p
.app
.usage
.as_ref()
.unwrap_or_else(|| self.0.app.bin_name.as_ref().unwrap_or(&self.0.app.name));
.unwrap_or_else(|| self.p.app.bin_name.as_ref().unwrap_or(&self.p.app.name));
usage.push_str(&*name);
let req_string = if incl_reqs {
let mut reqs: Vec<&str> = self.0.required().map(|r| &**r).collect();
reqs.sort();
reqs.dedup();
self.get_required_usage_from(&reqs, None, None, false)
self.get_required_usage_from(&[], None, true)
.iter()
.fold(String::new(), |a, s| a + &format!(" {}", s)[..])
} else {
@ -80,13 +61,13 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
};
let flags = self.needs_flags_tag();
if flags && !self.0.is_set(AS::UnifiedHelpMessage) {
if flags && !self.p.is_set(AS::UnifiedHelpMessage) {
usage.push_str(" [FLAGS]");
} else if flags {
usage.push_str(" [OPTIONS]");
}
if !self.0.is_set(AS::UnifiedHelpMessage)
&& opts!(self.0.app)
if !self.p.is_set(AS::UnifiedHelpMessage)
&& opts!(self.p.app)
.any(|o| !o.is_set(ArgSettings::Required) && !o.is_set(ArgSettings::Hidden))
{
usage.push_str(" [OPTIONS]");
@ -94,13 +75,13 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
usage.push_str(&req_string[..]);
let has_last = positionals!(self.0.app).any(|p| p.is_set(ArgSettings::Last));
let has_last = positionals!(self.p.app).any(|p| p.is_set(ArgSettings::Last));
// places a '--' in the usage string if there are args and options
// supporting multiple values
if opts!(self.0.app).any(|o| o.is_set(ArgSettings::MultipleValues))
&& positionals!(self.0.app).any(|p| !p.is_set(ArgSettings::Required))
&& !(self.0.app.has_visible_subcommands()
|| self.0.is_set(AS::AllowExternalSubcommands)) && !has_last
if opts!(self.p.app).any(|o| o.is_set(ArgSettings::MultipleValues))
&& positionals!(self.p.app).any(|p| !p.is_set(ArgSettings::Required))
&& !(self.p.app.has_visible_subcommands()
|| self.p.is_set(AS::AllowExternalSubcommands)) && !has_last
{
usage.push_str(" [--]");
}
@ -108,19 +89,19 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
(!p.is_set(ArgSettings::Required) || p.is_set(ArgSettings::Last))
&& !p.is_set(ArgSettings::Hidden)
};
if positionals!(self.0.app).any(not_req_or_hidden) {
if positionals!(self.p.app).any(not_req_or_hidden) {
if let Some(args_tag) = self.get_args_tag(incl_reqs) {
usage.push_str(&*args_tag);
} else {
usage.push_str(" [ARGS]");
}
if has_last && incl_reqs {
let pos = positionals!(self.0.app)
let pos = positionals!(self.p.app)
.find(|p| p.is_set(ArgSettings::Last))
.expect(INTERNAL_ERROR_MSG);
debugln!("Usage::create_help_usage: '{}' has .last(true)", pos.name);
let req = pos.is_set(ArgSettings::Required);
if req && positionals!(self.0.app).any(|p| !p.is_set(ArgSettings::Required)) {
if req && positionals!(self.p.app).any(|p| !p.is_set(ArgSettings::Required)) {
usage.push_str(" -- <");
} else if req {
usage.push_str(" [--] <");
@ -137,12 +118,12 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
}
// incl_reqs is only false when this function is called recursively
if self.0.app.has_visible_subcommands() && incl_reqs
|| self.0.is_set(AS::AllowExternalSubcommands)
if self.p.app.has_visible_subcommands() && incl_reqs
|| self.p.is_set(AS::AllowExternalSubcommands)
{
if self.0.is_set(AS::SubcommandsNegateReqs) || self.0.is_set(AS::ArgsNegateSubcommands)
if self.p.is_set(AS::SubcommandsNegateReqs) || self.p.is_set(AS::ArgsNegateSubcommands)
{
if !self.0.is_set(AS::ArgsNegateSubcommands) {
if !self.p.is_set(AS::ArgsNegateSubcommands) {
usage.push_str("\n ");
usage.push_str(&*self.create_help_usage(false));
usage.push_str(" <SUBCOMMAND>");
@ -151,8 +132,8 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
usage.push_str(&*name);
usage.push_str(" <SUBCOMMAND>");
}
} else if self.0.is_set(AS::SubcommandRequired)
|| self.0.is_set(AS::SubcommandRequiredElseHelp)
} else if self.p.is_set(AS::SubcommandRequired)
|| self.p.is_set(AS::SubcommandRequiredElseHelp)
{
usage.push_str(" <SUBCOMMAND>");
} else {
@ -169,24 +150,22 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
fn create_smart_usage(&self, used: &[&str]) -> String {
debugln!("usage::smart_usage;");
let mut usage = String::with_capacity(75);
let mut hs: Vec<&str> = self.0.required().map(|s| &**s).collect();
hs.extend_from_slice(used);
let r_string = self
.get_required_usage_from(&hs, None, None, false)
.get_required_usage_from(used, None, true)
.iter()
.fold(String::new(), |acc, s| acc + &format!(" {}", s)[..]);
usage.push_str(
&self
.0
.p
.app
.usage
.as_ref()
.unwrap_or_else(|| self.0.app.bin_name.as_ref().unwrap_or(&self.0.app.name))[..],
.unwrap_or_else(|| self.p.app.bin_name.as_ref().unwrap_or(&self.p.app.name))[..],
);
usage.push_str(&*r_string);
if self.0.is_set(AS::SubcommandRequired) {
if self.p.is_set(AS::SubcommandRequired) {
usage.push_str(" <SUBCOMMAND>");
}
usage.shrink_to_fit();
@ -197,19 +176,23 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
fn get_args_tag(&self, incl_reqs: bool) -> Option<String> {
debugln!("usage::get_args_tag;");
let mut count = 0;
'outer: for pos in positionals!(self.0.app)
'outer: for pos in positionals!(self.p.app)
.filter(|pos| !pos.is_set(ArgSettings::Required))
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
.filter(|pos| !pos.is_set(ArgSettings::Last))
{
debugln!("usage::get_args_tag:iter:{}:", pos.name);
if let Some(g_vec) = self.0.groups_for_arg(pos.name) {
for grp_s in &g_vec {
debugln!("usage::get_args_tag:iter:{}:iter:{};", pos.name, grp_s);
// if it's part of a required group we don't want to count it
if groups!(self.0.app).any(|g| g.required && (&g.name == grp_s)) {
continue 'outer;
}
for grp_s in groups_for_arg!(self.p.app, &pos.name) {
debugln!("usage::get_args_tag:iter:{}:iter:{};", pos.name, grp_s);
// if it's part of a required group we don't want to count it
if self
.p
.app
.groups
.iter()
.any(|g| g.required && (&g.name == &grp_s))
{
continue 'outer;
}
}
count += 1;
@ -218,11 +201,11 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
count
);
}
if !self.0.is_set(AS::DontCollapseArgsInUsage) && count > 1 {
if !self.p.is_set(AS::DontCollapseArgsInUsage) && count > 1 {
debugln!("usage::get_args_tag:iter: More than one, returning [ARGS]");
return None; // [ARGS]
} else if count == 1 && incl_reqs {
let pos = positionals!(self.0.app)
let pos = positionals!(self.p.app)
.find(|pos| {
!pos.is_set(ArgSettings::Required)
&& !pos.is_set(ArgSettings::Hidden)
@ -238,13 +221,13 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
pos.name_no_brackets(),
pos.multiple_str()
));
} else if self.0.is_set(AS::DontCollapseArgsInUsage)
&& self.0.has_positionals()
} else if self.p.is_set(AS::DontCollapseArgsInUsage)
&& self.p.has_positionals()
&& incl_reqs
{
debugln!("usage::get_args_tag:iter: Don't collapse returning all");
return Some(
positionals!(self.0.app)
positionals!(self.p.app)
.filter(|pos| !pos.is_set(ArgSettings::Required))
.filter(|pos| !pos.is_set(ArgSettings::Hidden))
.filter(|pos| !pos.is_set(ArgSettings::Last))
@ -254,7 +237,7 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
);
} else if !incl_reqs {
debugln!("usage::get_args_tag:iter: incl_reqs=false, building secondary usage string");
let highest_req_pos = positionals!(self.0.app)
let highest_req_pos = positionals!(self.p.app)
.filter_map(|pos| {
if pos.is_set(ArgSettings::Required) && !pos.is_set(ArgSettings::Last) {
Some(pos.index)
@ -263,9 +246,9 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
}
})
.max()
.unwrap_or_else(|| Some(positionals!(self.0.app).count() as u64));
.unwrap_or_else(|| Some(positionals!(self.p.app).count() as u64));
return Some(
positionals!(self.0.app)
positionals!(self.p.app)
.filter_map(|pos| {
if pos.index <= highest_req_pos {
Some(pos)
@ -287,7 +270,7 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
// Determines if we need the `[FLAGS]` tag in the usage string
fn needs_flags_tag(&self) -> bool {
debugln!("usage::needs_flags_tag;");
'outer: for f in flags!(self.0.app) {
'outer: for f in flags!(self.p.app) {
debugln!("usage::needs_flags_tag:iter: f={};", f.name);
if let Some(l) = f.long {
if l == "help" || l == "version" {
@ -295,13 +278,17 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
continue;
}
}
if let Some(g_vec) = self.0.groups_for_arg(f.name) {
for grp_s in &g_vec {
debugln!("usage::needs_flags_tag:iter:iter: grp_s={};", grp_s);
if groups!(self.0.app).any(|g| &g.name == grp_s && g.required) {
debugln!("usage::needs_flags_tag:iter:iter: Group is required");
continue 'outer;
}
for grp_s in groups_for_arg!(self.p.app, &f.name) {
debugln!("usage::needs_flags_tag:iter:iter: grp_s={};", grp_s);
if self
.p
.app
.groups
.iter()
.any(|g| &g.name == &grp_s && g.required)
{
debugln!("usage::needs_flags_tag:iter:iter: Group is required");
continue 'outer;
}
}
if f.is_set(ArgSettings::Hidden) {
@ -316,134 +303,68 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
}
// Returns the required args in usage string form by fully unrolling all groups
// `used`: The args that were used
// `incl`: Args that should be displayed even if not required
// `incl_last`: should we incldue args that are Arg::Last? (i.e. `prog [foo] -- [last]). We
// can't do that for required usages being built for subcommands because it would look like:
// `prog [foo] -- [last] <subcommand>` which is totally wrong.
pub fn get_required_usage_from(
&self,
reqs: &[&str],
matcher: Option<&ArgMatcher<'a>>,
extra: Option<&str>,
incls: &[&str],
matcher: Option<&ArgMatcher>,
incl_last: bool,
) -> VecDeque<String> {
debugln!(
"Usage::get_required_usage_from: reqs={:?}, extra={:?}",
reqs,
extra
);
let mut desc_reqs: Vec<&str> = vec![];
desc_reqs.extend(extra);
let mut new_reqs: Vec<&str> = vec![];
macro_rules! get_requires {
(@group $a:ident, $v:ident, $p:ident) => {{
if let Some(rl) = groups!(self.0.app)
.filter(|g| g.requires.is_some())
.find(|g| &g.name == $a)
.map(|g| g.requires.as_ref().unwrap())
{
for r in rl {
if !$p.contains(&r) {
debugln!(
"Usage::get_required_usage_from:iter:{}: adding group req={:?}",
$a,
r
);
$v.push(r);
}
}
}
}};
($a:ident, $what:ident, $how:ident, $v:ident, $p:ident) => {{
if let Some(rl) = $what!(self.0.app)
.filter(|a| a.requires.is_some())
.find(|arg| &arg.name == $a)
.map(|a| a.requires.as_ref().unwrap())
{
for &(_, r) in rl.iter() {
if !$p.contains(&r) {
debugln!(
"usage::get_required_usage_from:iter:{}: adding arg req={:?}",
$a,
r
);
$v.push(r);
}
}
}
}};
}
// initialize new_reqs
for a in reqs {
get_requires!(a, flags, iter, new_reqs, reqs);
get_requires!(a, opts, iter, new_reqs, reqs);
get_requires!(a, positionals, values, new_reqs, reqs);
get_requires!(@group a, new_reqs, reqs);
}
desc_reqs.extend_from_slice(&*new_reqs);
debugln!(
"usage::get_required_usage_from: after init desc_reqs={:?}",
desc_reqs
);
loop {
let mut tmp = vec![];
for a in &new_reqs {
get_requires!(a, flags, iter, tmp, desc_reqs);
get_requires!(a, opts, iter, tmp, desc_reqs);
get_requires!(a, positionals, values, tmp, desc_reqs);
get_requires!(@group a, tmp, desc_reqs);
}
if tmp.is_empty() {
debugln!("usage::get_required_usage_from: no more children");
break;
} else {
debugln!("usage::get_required_usage_from: after iter tmp={:?}", tmp);
debugln!(
"usage::get_required_usage_from: after iter new_reqs={:?}",
new_reqs
);
desc_reqs.extend_from_slice(&*new_reqs);
new_reqs.clear();
new_reqs.extend_from_slice(&*tmp);
debugln!(
"Usage::get_required_usage_from: after iter desc_reqs={:?}",
desc_reqs
);
}
}
desc_reqs.extend_from_slice(reqs);
desc_reqs.sort();
desc_reqs.dedup();
debugln!(
"Usage::get_required_usage_from: final desc_reqs={:?}",
desc_reqs
"Usage::get_required_usage_from: incls={:?}, matcher={:?}, incl_last={:?}",
incls,
matcher.is_some(),
incl_last
);
let mut ret_val = VecDeque::new();
let args_in_groups = groups!(self.0.app)
.filter(|gn| desc_reqs.contains(&gn.name))
.flat_map(|g| self.0.arg_names_in_group(g.name))
let mut unrolled_reqs = vec![];
for a in self.p.required.iter() {
if let Some(ref m) = matcher {
for aa in self.p.app.unroll_requirements_for_arg(a, m) {
unrolled_reqs.push(aa);
}
} else {
unrolled_reqs.push(a);
}
}
let args_in_groups = self
.p
.app
.groups
.iter()
.filter(|gn| self.p.required.contains(&gn.name))
.flat_map(|g| self.p.app.unroll_args_in_group(g.name))
.collect::<Vec<_>>();
let pmap = if let Some(m) = matcher {
desc_reqs
unrolled_reqs
.iter()
.filter(|a| self.0.positionals.values().any(|p| &p == a))
.chain(incls.iter())
.filter(|a| self.p.positionals.values().any(|p| &p == a))
.filter(|&pos| !m.contains(pos))
.filter_map(|pos| find!(self.0.app, pos))
.filter_map(|pos| self.p.app.find(pos))
.filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
.filter(|pos| !args_in_groups.contains(&pos.name))
.map(|pos| (pos.index.unwrap(), pos))
.collect::<BTreeMap<u64, &Arg>>() // sort by index
} else {
desc_reqs
unrolled_reqs
.iter()
.filter(|a| self.0.positionals.values().any(|p| &p == a))
.filter_map(|pos| find!(self.0.app, pos))
.chain(incls.iter())
.filter(|a| self.p.positionals.values().any(|p| &p == a))
.filter_map(|pos| self.p.app.find(pos))
.filter(|&pos| incl_last || !pos.is_set(ArgSettings::Last))
.filter(|pos| !args_in_groups.contains(&pos.name))
.map(|pos| (pos.index.unwrap(), pos))
.collect::<BTreeMap<u64, &Arg>>() // sort by index
};
debugln!(
"Usage::get_required_usage_from: args_in_groups={:?}",
args_in_groups
);
for &p in pmap.values() {
debugln!("Usage::get_required_usage_from:iter:{}", p.to_string());
let s = p.to_string();
@ -451,25 +372,29 @@ impl<'a, 'b, 'c, 'z> Usage<'a, 'b, 'c, 'z> {
ret_val.push_back(s);
}
}
for a in desc_reqs
for a in unrolled_reqs
.iter()
.filter(|name| !positionals!(self.0.app).any(|p| &&p.name == name))
.filter(|name| !groups!(self.0.app).any(|g| &&g.name == name))
.chain(incls.iter())
.filter(|name| !positionals!(self.p.app).any(|p| &&p.name == name))
.filter(|name| !self.p.app.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!("Usage::get_required_usage_from:iter:{}:", a);
let arg = find!(self.0.app, a)
let arg = self
.p
.app
.find(a)
.map(|f| f.to_string())
.expect(INTERNAL_ERROR_MSG);
ret_val.push_back(arg);
}
let mut g_vec: Vec<String> = vec![];
for g in desc_reqs
for g in unrolled_reqs
.iter()
.filter(|n| groups!(self.0.app).any(|g| &&g.name == n))
.filter(|n| self.p.app.groups.iter().any(|g| &&g.name == n))
{
let g_string = self.0.args_in_group(g).join("|");
let g_string = self.p.app.unroll_args_in_group(g).join("|");
let elem = format!("<{}>", &g_string[..g_string.len()]);
if !g_vec.contains(&elem) {
g_vec.push(elem);

View file

@ -1,5 +1,5 @@
// Std
use std::ffi::OsString;
use std::ffi::{OsStr, OsString};
#[doc(hidden)]
#[derive(Debug, Clone)]
@ -24,4 +24,10 @@ impl Default for MatchedArg {
impl MatchedArg {
pub fn new() -> Self { MatchedArg::default() }
pub(crate) fn contains_val(&self, val: &str) -> bool {
self.vals
.iter()
.map(|v| v.as_os_str())
.any(|v| v == OsStr::new(val))
}
}

View file

@ -26,7 +26,7 @@ use util::VecMap;
// Internal
use build::app::Propagation;
use build::AppSettings as AS;
use build::{App, Arg, ArgSettings};
use build::{App, Arg, ArgGroup, ArgSettings};
use output::Help;
use output::Usage;
use parse::errors::Error as ClapError;
@ -35,7 +35,7 @@ use parse::errors::Result as ClapResult;
use parse::features::suggestions;
use parse::Validator;
use parse::{ArgMatcher, SubCommand};
use util::OsStrExt2;
use util::{ChildGraph, OsStrExt2};
use INVALID_UTF8;
use INTERNAL_ERROR_MSG;
@ -58,8 +58,8 @@ where
'b: 'c,
{
pub app: &'c mut App<'a, 'b>,
pub required: Vec<&'a str>,
pub r_ifs: Vec<(&'a str, &'b str, &'a str)>,
pub required: ChildGraph<&'a str>,
// pub r_ifs: Vec<(&'a str, &'b str, &'a str)>,
pub overriden: Vec<&'a str>,
cache: Option<&'a str>,
num_opts: usize,
@ -102,17 +102,20 @@ where
'b: 'c,
{
pub fn new(app: &'c mut App<'a, 'b>) -> Self {
let reqs = app
let mut reqs = ChildGraph::with_capacity(5);
for a in app
.args
.iter()
.filter(|a| a.settings.is_set(ArgSettings::Required))
.map(|a| a.name)
.collect();
{
reqs.insert(a);
}
Parser {
app: app,
required: reqs,
r_ifs: Vec::new(),
required: ChildGraph::from(reqs),
// r_ifs: Vec::new(),
overriden: Vec::new(),
cache: None,
num_opts: 0,
@ -165,8 +168,11 @@ where
// find() (iterator, not macro) gets called repeatedly.
let last_name = it.next().expect(INTERNAL_ERROR_MSG);
let second_to_last_name = it.next().expect(INTERNAL_ERROR_MSG);
let last = find!(self.app, last_name).expect(INTERNAL_ERROR_MSG);
let second_to_last = find!(self.app, second_to_last_name).expect(INTERNAL_ERROR_MSG);
let last = self.app.find(last_name).expect(INTERNAL_ERROR_MSG);
let second_to_last = self
.app
.find(second_to_last_name)
.expect(INTERNAL_ERROR_MSG);
// Either the final positional is required
// Or the second to last has a terminator or .last(true) set
@ -218,7 +224,7 @@ where
.positionals
.values()
.rev()
.map(|p_name| find!(self.app, p_name).expect(INTERNAL_ERROR_MSG))
.map(|p_name| self.app.find(p_name).expect(INTERNAL_ERROR_MSG))
{
if foundx2 && !p.is_set(ArgSettings::Required) {
assert!(
@ -254,7 +260,7 @@ where
.positionals
.values()
.rev()
.map(|p_name| find!(self.app, p_name).expect(INTERNAL_ERROR_MSG))
.map(|p_name| self.app.find(p_name).expect(INTERNAL_ERROR_MSG))
{
if found {
assert!(
@ -305,14 +311,16 @@ where
for a in &mut self.app.args {
// Add conditional requirements
if let Some(ref r_ifs) = a.r_ifs {
for &(arg, val) in r_ifs {
self.r_ifs.push((arg, val, a.name));
}
}
// if let Some(ref r_ifs) = a.r_ifs {
// for &(arg, val) in r_ifs {
// self.r_ifs.push((arg, val, a.name));
// }
// }
// Add args with default requirements
if a.is_set(ArgSettings::Required) {
debugln!("Parser::_build: adding {} to default requires", a.name);
let idx = self.required.insert(a.name);
// If the arg is required, add all it's requirements to master required list
if let Some(ref areqs) = a.requires {
for name in areqs
@ -320,10 +328,9 @@ where
.filter(|&&(val, _)| val.is_none())
.map(|&(_, name)| name)
{
self.required.push(name);
self.required.insert_child(idx, name);
}
}
self.required.push(a.name);
}
count_arg(
@ -340,7 +347,9 @@ where
a.is_set(ArgSettings::MultipleValues)
&& (a.index.unwrap_or(0) as usize != self.positionals.len())
}) && self.positionals.values().last().map_or(false, |p_name| {
!find!(self.app, p_name)
!self
.app
.find(p_name)
.expect(INTERNAL_ERROR_MSG)
.is_set(ArgSettings::Last)
}) {
@ -349,9 +358,11 @@ where
for group in &self.app.groups {
if group.required {
self.required.push(group.name);
let idx = self.required.insert(group.name);
if let Some(ref reqs) = group.requires {
self.required.extend_from_slice(reqs);
for a in reqs {
self.required.insert_child(idx, a);
}
}
}
}
@ -465,7 +476,7 @@ where
return Err(ClapError::unknown_argument(
&*arg_os.to_string_lossy(),
"",
&*Usage::new(self).create_error_usage(matcher, None),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
@ -479,7 +490,7 @@ where
} else {
if let ParseResult::Opt(name) = needs_val_of {
// Check to see if parsing a value from a previous arg
let arg = find!(self.app, &name).expect(INTERNAL_ERROR_MSG);
let arg = self.app.find(&name).expect(INTERNAL_ERROR_MSG);
// get the option so we can check the settings
needs_val_of = self.add_val_to_arg(arg, &arg_os, matcher)?;
// get the next value from the iterator
@ -499,7 +510,7 @@ where
arg_os.to_string_lossy().into_owned(),
cdate,
self.app.bin_name.as_ref().unwrap_or(&self.app.name),
&*Usage::new(self).create_error_usage(matcher, None),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
@ -557,7 +568,7 @@ where
return Err(ClapError::unknown_argument(
&*arg_os.to_string_lossy(),
"",
&*Usage::new(self).create_error_usage(matcher, None),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
@ -572,9 +583,9 @@ where
let _ = self.add_val_to_arg(p, &arg_os, matcher)?;
matcher.inc_occurrence_of(p.name);
let _ = self
.groups_for_arg(p.name)
.and_then(|vec| Some(matcher.inc_occurrences_of(&*vec)));
for grp in groups_for_arg!(self.app, &p.name) {
matcher.inc_occurrence_of(&*grp);
}
self.app.settings.set(AS::ValidArgFound);
// Only increment the positional counter if it doesn't allow multiples
@ -589,7 +600,7 @@ where
None => {
if !self.is_set(AS::StrictUtf8) {
return Err(ClapError::invalid_utf8(
&*Usage::new(self).create_error_usage(matcher, None),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
@ -603,7 +614,7 @@ where
let a = v.into();
if a.to_str().is_none() && !self.is_set(AS::StrictUtf8) {
return Err(ClapError::invalid_utf8(
&*Usage::new(self).create_error_usage(matcher, None),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
@ -622,7 +633,7 @@ where
return Err(ClapError::unknown_argument(
&*arg_os.to_string_lossy(),
"",
&*Usage::new(self).create_error_usage(matcher, None),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
} else if !has_args || self.is_set(AS::InferSubcommands) && self.has_subcommands() {
@ -633,7 +644,7 @@ where
arg_os.to_string_lossy().into_owned(),
cdate,
self.app.bin_name.as_ref().unwrap_or(&self.app.name),
&*Usage::new(self).create_error_usage(matcher, None),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
} else {
@ -647,7 +658,7 @@ where
return Err(ClapError::unknown_argument(
&*arg_os.to_string_lossy(),
"",
&*Usage::new(self).create_error_usage(matcher, None),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
@ -668,7 +679,7 @@ where
let bn = self.app.bin_name.as_ref().unwrap_or(&self.app.name);
return Err(ClapError::missing_subcommand(
bn,
&Usage::new(self).create_error_usage(matcher, None),
&Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
} else if self.is_set(AS::SubcommandRequiredElseHelp) {
@ -799,11 +810,11 @@ where
};
let arg_allows_tac = match needs_val_of {
ParseResult::Opt(name) => {
let o = find!(self.app, &name).expect(INTERNAL_ERROR_MSG);
let o = self.app.find(&name).expect(INTERNAL_ERROR_MSG);
(o.is_set(ArgSettings::AllowHyphenValues) || app_wide_settings)
}
ParseResult::Pos(name) => {
let p = find!(self.app, &name).expect(INTERNAL_ERROR_MSG);
let p = self.app.find(&name).expect(INTERNAL_ERROR_MSG);
(p.is_set(ArgSettings::AllowHyphenValues) || app_wide_settings)
}
ParseResult::ValuesDone => return true,
@ -849,11 +860,7 @@ where
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();
for k in matcher.arg_names() {
hs.push(k);
}
let reqs = Usage::new(self).get_required_usage_from(&hs, Some(matcher), None, false);
let reqs = Usage::new(self).get_required_usage_from(&[], None, true); // maybe Some(m)
for s in &reqs {
write!(&mut mid_string, " {}", s).expect(INTERNAL_ERROR_MSG);
@ -1141,7 +1148,7 @@ where
return Err(ClapError::unknown_argument(
&*arg,
"",
&*Usage::new(self).create_error_usage(matcher, None),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
@ -1172,7 +1179,7 @@ where
sdebugln!("Found Empty - Error");
return Err(ClapError::empty_value(
opt,
&*Usage::new(self).create_error_usage(matcher, None),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
}
@ -1187,7 +1194,7 @@ where
sdebugln!("None, but requires equals...Error");
return Err(ClapError::empty_value(
opt,
&*Usage::new(self).create_error_usage(matcher, None),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
));
} else {
@ -1196,8 +1203,9 @@ where
matcher.inc_occurrence_of(opt.name);
// Increment or create the group "args"
self.groups_for_arg(opt.name)
.and_then(|vec| Some(matcher.inc_occurrences_of(&*vec)));
for grp in groups_for_arg!(self.app, &opt.name) {
matcher.inc_occurrence_of(&*grp);
}
let needs_delim = opt.is_set(ArgSettings::RequireDelimiter);
let mult = opt.is_set(ArgSettings::MultipleValues);
@ -1273,10 +1281,8 @@ where
matcher.add_index_to(arg.name, self.cur_idx.get());
// Increment or create the group "args"
if let Some(grps) = self.groups_for_arg(arg.name) {
for grp in grps {
matcher.add_val_to(&*grp, v);
}
for grp in groups_for_arg!(self.app, &arg.name) {
matcher.add_val_to(&*grp, v);
}
if matcher.needs_more_vals(arg) {
@ -1296,20 +1302,21 @@ where
matcher.add_index_to(flag.name, self.cur_idx.get());
// Increment or create the group "args"
self.groups_for_arg(flag.name)
.and_then(|vec| Some(matcher.inc_occurrences_of(&*vec)));
for grp in groups_for_arg!(self.app, &flag.name) {
matcher.inc_occurrence_of(grp);
}
Ok(ParseResult::Flag)
}
fn remove_overrides(&mut self, matcher: &mut ArgMatcher) {
fn remove_overrides(&mut self, matcher: &mut ArgMatcher<'a>) {
debugln!("Parser::remove_overrides;");
let mut to_rem: Vec<&str> = Vec::new();
let mut self_override: Vec<&str> = Vec::new();
let mut arg_overrides = Vec::new();
for name in matcher.arg_names() {
debugln!("Parser::remove_overrides:iter:{};", name);
if let Some(arg) = find!(self.app, name) {
if let Some(arg) = self.app.find(name) {
let mut handle_self_override = |o| {
if (arg.is_set(ArgSettings::MultipleValues)
|| arg.is_set(ArgSettings::MultipleOccurrences))
@ -1511,12 +1518,14 @@ where
// Add the arg to the matches to build a proper usage string
if let Some(name) = suffix.1 {
if let Some(opt) = find_by_long!(self.app, name) {
self.groups_for_arg(&*opt.name)
.and_then(|grps| Some(matcher.inc_occurrences_of(&*grps)));
for grp in groups_for_arg!(self.app, &opt.name) {
matcher.inc_occurrence_of(&*grp);
}
matcher.insert(&*opt.name);
} else if let Some(flg) = find_by_long!(self.app, name) {
self.groups_for_arg(&*flg.name)
.and_then(|grps| Some(matcher.inc_occurrences_of(&*grps)));
for grp in groups_for_arg!(self.app, &flg.name) {
matcher.inc_occurrence_of(&*grp);
}
matcher.insert(&*flg.name);
}
}
@ -1525,7 +1534,7 @@ where
Err(ClapError::unknown_argument(
&*used_arg,
&*suffix.0,
&*Usage::new(self).create_error_usage(matcher, None),
&*Usage::new(self).create_usage_with_title(&[]),
self.app.color(),
))
}
@ -1578,78 +1587,11 @@ where
'a: 'b,
'b: 'c,
{
pub(crate) fn groups_for_arg(&self, name: &str) -> Option<Vec<&'a str>> {
debugln!("Parser::groups_for_arg: name={}", name);
if self.app.groups.is_empty() {
debugln!("Parser::groups_for_arg: No groups defined");
return None;
}
let mut res = vec![];
debugln!("Parser::groups_for_arg: Searching through groups...");
for grp in groups!(self.app) {
for a in &grp.args {
if a == &name {
sdebugln!("\tFound '{}'", grp.name);
res.push(&*grp.name);
}
}
}
if res.is_empty() {
return None;
}
Some(res)
}
pub(crate) fn args_in_group(&self, group: &str) -> Vec<String> {
let mut g_vec = vec![];
let mut args = vec![];
for n in &find!(self.app, &group, groups)
.expect(INTERNAL_ERROR_MSG)
.args
{
if let Some(ref f) = find!(self.app, n) {
if f.index.is_some() {
args.push(f.name.to_owned());
} else {
args.push(f.to_string());
}
} else {
g_vec.push(n);
}
}
for av in g_vec.iter().map(|g| self.args_in_group(g)) {
args.extend(av);
}
args.dedup();
args.iter().map(ToOwned::to_owned).collect()
}
pub(crate) fn arg_names_in_group(&self, group: &str) -> Vec<&'a str> {
let mut g_vec = vec![];
let mut args = vec![];
for n in &find!(self.app, &group, groups)
.expect(INTERNAL_ERROR_MSG)
.args
{
if groups!(self.app).any(|g| &g.name == n) {
args.extend(self.arg_names_in_group(n));
g_vec.push(n);
} else if !args.contains(&&n) {
args.push(n);
}
}
args.iter().map(|s| *s).collect()
}
fn contains_short(&self, s: char) -> bool { self.app.contains_short(s) }
pub(crate) fn required(&self) -> Iter<&str> { self.required.iter() }
pub(crate) fn required(&self) -> impl Iterator<Item = &str> {
self.required.iter().map(|s| &**s)
}
#[cfg_attr(feature = "lints", allow(needless_borrow))]
pub(crate) fn has_args(&self) -> bool { self.app.has_args() }

View file

@ -1,6 +1,7 @@
// std
#[allow(unused_imports)]
use std::ascii::AsciiExt;
use std::ffi::OsStr;
// Internal
use build::app::AppSettings as AS;
@ -10,17 +11,27 @@ use output::Usage;
use parse::errors::Result as ClapResult;
use parse::errors::{Error, ErrorKind};
use parse::{ArgMatcher, MatchedArg, ParseResult, Parser};
use util::ChildGraph;
use INVALID_UTF8;
use INTERNAL_ERROR_MSG;
pub struct Validator<'a, 'b, 'c, 'z>(&'z mut Parser<'a, 'b, 'c>)
pub struct Validator<'a, 'b, 'c, 'z>
where
'a: 'b,
'b: 'c,
'c: 'z;
'c: 'z,
{
p: &'z mut Parser<'a, 'b, 'c>,
c: ChildGraph<&'a str>,
}
impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
pub fn new(p: &'z mut Parser<'a, 'b, 'c>) -> Self { Validator(p) }
pub fn new(p: &'z mut Parser<'a, 'b, 'c>) -> Self {
Validator {
p: p,
c: ChildGraph::with_capacity(5),
}
}
pub fn validate(
&mut self,
@ -30,12 +41,14 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
) -> ClapResult<()> {
debugln!("Validator::validate;");
let mut reqs_validated = false;
self.0.add_env(matcher)?;
self.0.add_defaults(matcher)?;
self.p.add_env(matcher)?;
self.p.add_defaults(matcher)?;
if let ParseResult::Opt(a) = needs_val_of {
debugln!("Validator::validate: needs_val_of={:?}", a);
let o = find!(self.0.app, &a).expect(INTERNAL_ERROR_MSG);
self.validate_required(matcher)?;
{
self.validate_required(matcher)?;
}
let o = self.p.app.find(&a).expect(INTERNAL_ERROR_MSG);
reqs_validated = true;
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)
@ -45,27 +58,28 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
if should_err {
return Err(Error::empty_value(
o,
&*Usage::new(self.0).create_error_usage(matcher, None),
self.0.app.color(),
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
}
if matcher.is_empty()
&& matcher.subcommand_name().is_none()
&& self.0.is_set(AS::ArgRequiredElseHelp)
&& self.p.is_set(AS::ArgRequiredElseHelp)
{
let mut out = vec![];
self.0.write_help_err(&mut out)?;
self.p.write_help_err(&mut out)?;
return Err(Error {
message: String::from_utf8_lossy(&*out).into_owned(),
kind: ErrorKind::MissingArgumentOrSubcommand,
info: None,
});
}
self.validate_blacklist(matcher)?;
if !(self.0.is_set(AS::SubcommandsNegateReqs) && subcmd_name.is_some()) && !reqs_validated {
self.validate_conflicts(matcher)?;
if !(self.p.is_set(AS::SubcommandsNegateReqs) && subcmd_name.is_some()) && !reqs_validated {
self.validate_required(matcher)?;
self.validate_required_unless(matcher)?;
}
self.validate_matched_args(matcher)?;
@ -80,14 +94,14 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
) -> ClapResult<()> {
debugln!("Validator::validate_arg_values: arg={:?}", arg.name);
for val in &ma.vals {
if self.0.is_set(AS::StrictUtf8) && val.to_str().is_none() {
if self.p.is_set(AS::StrictUtf8) && val.to_str().is_none() {
debugln!(
"Validator::validate_arg_values: invalid UTF-8 found in val {:?}",
val
);
return Err(Error::invalid_utf8(
&*Usage::new(self.0).create_error_usage(matcher, None),
self.0.app.color(),
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
if let Some(ref p_vals) = arg.possible_vals {
@ -103,8 +117,8 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
val_str,
p_vals,
arg,
&*Usage::new(self.0).create_error_usage(matcher, None),
self.0.app.color(),
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
}
@ -115,15 +129,15 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
debugln!("Validator::validate_arg_values: illegal empty val found");
return Err(Error::empty_value(
arg,
&*Usage::new(self.0).create_error_usage(matcher, None),
self.0.app.color(),
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
if let Some(ref vtor) = arg.validator {
debug!("Validator::validate_arg_values: checking validator...");
if let Err(e) = vtor(val.to_string_lossy().into_owned()) {
sdebugln!("error");
return Err(Error::value_validation(Some(arg), e, self.0.app.color()));
return Err(Error::value_validation(Some(arg), e, self.p.app.color()));
} else {
sdebugln!("good");
}
@ -135,7 +149,7 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
return Err(Error::value_validation(
Some(arg),
(*e).to_string(),
self.0.app.color(),
self.p.app.color(),
));
} else {
sdebugln!("good");
@ -147,53 +161,54 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
fn build_conflict_err(&self, name: &str, matcher: &ArgMatcher<'a>) -> ClapResult<()> {
debugln!("build_err!: name={}", name);
let mut c_with = find_from!(self.0.app, &name, blacklist, &matcher);
c_with = c_with.or(find!(self.0.app, &name)
let mut c_with = find_from!(self.p.app, &name, blacklist, &matcher);
c_with = c_with.or(self
.p
.app
.find(&name)
.map_or(None, |ref aa| aa.blacklist.as_ref())
.map_or(None, |ref bl| bl.iter().find(|arg| matcher.contains(arg)))
.map_or(None, |an| find!(self.0.app, an))
.map_or(None, |an| self.p.app.find(an))
.map_or(None, |aa| Some(format!("{}", aa))));
debugln!("build_err!: '{:?}' conflicts with '{}'", c_with, &name);
// matcher.remove(&name);
let usg = Usage::new(self.0).create_error_usage(matcher, None);
if let Some(f) = find!(self.0.app, &name) {
let usg = Usage::new(self.p).create_usage_with_title(&[]);
if let Some(f) = self.p.app.find(&name) {
debugln!("build_err!: It was a flag...");
Err(Error::argument_conflict(
f,
c_with,
&*usg,
self.0.app.color(),
self.p.app.color(),
))
} else {
panic!(INTERNAL_ERROR_MSG);
}
}
fn validate_blacklist(&self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
debugln!("Validator::validate_blacklist;");
for name in &self.gather_conflicts(matcher) {
debugln!("Validator::validate_blacklist:iter:{};", name);
fn validate_conflicts(&mut self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
debugln!("Validator::validate_conflicts;");
self.gather_conflicts(matcher);
for name in self.c.iter() {
debugln!("Validator::validate_conflicts:iter:{};", name);
let mut should_err = false;
if groups!(self.0.app).any(|g| &g.name == name) {
debugln!("Validator::validate_blacklist:iter:{}:group;", name);
for n in self.0.arg_names_in_group(name) {
debugln!(
"Validator::validate_blacklist:iter:{}:group:iter:{};",
name,
n
);
if matcher.contains(n) {
debugln!(
"Validator::validate_blacklist:iter:{}:group:iter:{}: found;",
name,
n
);
return self.build_conflict_err(n, matcher);
}
}
if let Some(g) = self
.p
.app
.groups
.iter()
.filter(|g| !g.multiple)
.find(|g| &g.name == name)
{
should_err = self
.p
.app
.unroll_args_in_group(g.name)
.iter()
.filter(|a| matcher.contains(a))
.count() > 1;
} else if let Some(ma) = matcher.get(name) {
debugln!(
"Validator::validate_blacklist:iter:{}: matcher contains it...",
"Validator::validate_conflicts:iter:{}: matcher contains it...",
name
);
should_err = ma.occurs > 0;
@ -205,58 +220,87 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
Ok(())
}
fn gather_conflicts(&self, matcher: &mut ArgMatcher<'a>) -> Vec<&'a str> {
// Gathers potential conflicts based on used argument, but without considering requirements
// and such
fn gather_conflicts(&mut self, matcher: &mut ArgMatcher<'a>) {
debugln!("Validator::gather_conflicts;");
let mut conflicts = vec![];
for name in matcher.arg_names() {
debugln!("Validator::gather_conflicts:iter:{};", name);
if let Some(arg) = find!(self.0.app, name) {
if let Some(arg) = self.p.app.find(name) {
// Since an arg was used, every arg it conflicts with is added to the conflicts
if let Some(ref bl) = arg.blacklist {
for conf in bl {
if matcher.get(conf).is_some() {
conflicts.push(*conf);
if self.p.app.find(conf).is_some() {
if conf != name {
self.c.insert(conf);
}
} else {
// for g_arg in self.p.app.unroll_args_in_group(conf) {
// if &g_arg != name {
self.c.insert(conf); // TODO ERROR is here - groups allow one arg but this line disallows all group args
// }
// }
}
}
}
if let Some(grps) = self.0.groups_for_arg(name) {
for grp in &grps {
if let Some(g) = find!(self.0.app, grp, groups) {
if !g.multiple {
for g_arg in &g.args {
if &g_arg == &name {
continue;
}
conflicts.push(g_arg);
}
}
if let Some(ref gc) = g.conflicts {
conflicts.extend(&*gc);
}
}
// Now we need to know which groups this arg was a memeber of, to add all other
// args in that group to the conflicts, as well as any args those args conflict
// with
for grp in groups_for_arg!(self.p.app, name) {
if let Some(g) = self
.p
.app
.groups
.iter()
.filter(|g| !g.multiple)
.find(|g| g.name == grp)
{
// for g_arg in self.p.app.unroll_args_in_group(&g.name) {
// if &g_arg != name {
self.c.insert(g.name);
// }
// }
}
}
} else {
debugln!("Validator::gather_conflicts:iter:{}:group;", name);
let args = self.0.arg_names_in_group(name);
for arg in &args {
debugln!(
"Validator::gather_conflicts:iter:{}:group:iter:{};",
name,
arg
);
if let Some(ref bl) =
find!(self.0.app, arg).expect(INTERNAL_ERROR_MSG).blacklist
{
for conf in bl {
if matcher.get(conf).is_some() {
conflicts.push(conf);
}
if let Some(g) = self
.p
.app
.groups
.iter()
.filter(|g| !g.multiple)
.find(|grp| &grp.name == name)
{
// for g_arg in self.p.app.unroll_args_in_group(&g.name) {
self.c.insert(g.name);
// }
}
}
}
}
fn gather_requirements(&mut self, matcher: &ArgMatcher<'a>) {
debugln!("Validator::gather_requirements;");
for name in matcher.arg_names() {
debugln!("Validator::gather_requirements:iter:{};", name);
if let Some(arg) = self.p.app.find(name) {
for req in self.p.app.unroll_requirements_for_arg(arg.name, matcher) {
self.p.required.insert(req);
}
} else {
debugln!("Validator::gather_conflicts:iter:{}:group;", name);
if let Some(g) = self.p.app.groups.iter().find(|grp| &grp.name == name) {
if let Some(ref reqs) = g.requires {
for r in reqs {
// for g_arg in self.p.app.unroll_requirements_for_arg(r, matcher) {
self.p.required.insert(r);
// }
}
}
}
}
}
conflicts
}
fn validate_matched_args(&self, matcher: &mut ArgMatcher<'a>) -> ClapResult<()> {
@ -267,16 +311,22 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
name,
ma.vals
);
if let Some(arg) = find!(self.0.app, name) {
if let Some(arg) = self.p.app.find(name) {
self.validate_arg_num_vals(arg, ma, matcher)?;
self.validate_arg_values(arg, ma, matcher)?;
self.validate_arg_requires(arg, ma, matcher)?;
self.validate_arg_num_occurs(arg, ma, matcher)?;
} else {
let grp = find!(self.0.app, name, groups).expect(INTERNAL_ERROR_MSG);
let grp = self
.p
.app
.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);
return self.missing_required_error(matcher, Some(name));
}
}
}
@ -295,8 +345,8 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
// Not the first time, and we don't allow multiples
return Err(Error::unexpected_multiple_usage(
a,
&*Usage::new(self.0).create_error_usage(matcher, None),
self.0.app.color(),
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
Ok(())
@ -334,8 +384,8 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
} else {
"ere"
},
&*Usage::new(self.0).create_error_usage(matcher, None),
self.0.app.color(),
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
}
@ -351,8 +401,8 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
.to_str()
.expect(INVALID_UTF8),
a,
&*Usage::new(self.0).create_error_usage(matcher, None),
self.0.app.color(),
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
}
@ -364,8 +414,8 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
a,
num,
ma.vals.len(),
&*Usage::new(self.0).create_error_usage(matcher, None),
self.0.app.color(),
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
num == 0
@ -377,8 +427,8 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
if a.is_set(ArgSettings::TakesValue) && !min_vals_zero && ma.vals.is_empty() {
return Err(Error::empty_value(
a,
&*Usage::new(self.0).create_error_usage(matcher, None),
self.0.app.color(),
&*Usage::new(self.p).create_usage_with_title(&[]),
self.p.app.color(),
));
}
Ok(())
@ -386,7 +436,7 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
fn validate_arg_requires(
&self,
a: &Arg,
a: &Arg<'a, 'b>,
ma: &MatchedArg,
matcher: &ArgMatcher<'a>,
) -> ClapResult<()> {
@ -396,7 +446,7 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
let missing_req =
|v| v == val.expect(INTERNAL_ERROR_MSG) && !matcher.contains(name);
if ma.vals.iter().any(missing_req) {
return self.missing_required_error(matcher, None);
return self.missing_required_error(matcher, Some(a.name));
}
}
for &(_, name) in a_reqs.iter().filter(|&&(val, _)| val.is_none()) {
@ -408,30 +458,46 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
Ok(())
}
fn validate_required(&self, matcher: &ArgMatcher<'a>) -> ClapResult<()> {
fn validate_required(&mut self, matcher: &ArgMatcher<'a>) -> ClapResult<()> {
debugln!(
"Validator::validate_required: required={:?};",
self.0.required
self.p.required
);
self.gather_requirements(matcher);
'outer: for name in &self.0.required {
debugln!("Validator::validate_required:iter:{}:", name);
if matcher.contains(name) {
continue 'outer;
}
if let Some(a) = find!(self.0.app, name) {
if self.is_missing_required_ok(a, matcher) {
continue 'outer;
for arg_or_group in self.p.required.iter().filter(|r| !matcher.contains(r)) {
debugln!("Validator::validate_required:iter:aog={:?};", arg_or_group);
if let Some(arg) = self.p.app.find(arg_or_group) {
if !self.is_missing_required_ok(arg, matcher) {
return self.missing_required_error(matcher, None);
}
} else if let Some(group) = self.p.app.groups.iter().find(|g| &g.name == arg_or_group) {
if !self
.p
.app
.unroll_args_in_group(group.name)
.iter()
.any(|a| matcher.contains(a))
{
return self.missing_required_error(matcher, None);
}
}
return self.missing_required_error(matcher, None);
}
// Validate the conditionally required args
for &(a, v, r) in &self.0.r_ifs {
if let Some(ma) = matcher.get(a) {
if matcher.get(r).is_none() && ma.vals.iter().any(|val| val == v) {
return self.missing_required_error(matcher, Some(r));
for (a, r_ifs) in self
.p
.app
.args
.iter()
.filter(|a| a.r_ifs.is_some())
.map(|a| (a, a.r_ifs.as_ref().unwrap()))
{
for (other, val) in r_ifs.iter() {
if let Some(ma) = matcher.get(other) {
if ma.contains_val(val) && !matcher.contains(a.name) {
return self.missing_required_error(matcher, Some(a.name));
}
}
}
}
@ -439,78 +505,128 @@ impl<'a, 'b, 'c, 'z> Validator<'a, 'b, 'c, 'z> {
}
fn is_missing_required_ok(&self, a: &Arg<'a, 'b>, matcher: &ArgMatcher<'a>) -> bool {
debugln!("Validator::is_missing_required_ok: a={}", a.name);
self.validate_arg_conflicts(a, matcher).unwrap_or(false)
|| self.validate_required_unless(a, matcher).unwrap_or(false)
|| self.0.overriden.contains(&a.name)
debugln!("Validator::is_missing_required_ok: {}", a.name);
self.validate_arg_conflicts(a, matcher) || self.p.overriden.contains(&a.name)
}
fn validate_arg_conflicts(&self, a: &Arg<'a, 'b>, matcher: &ArgMatcher<'a>) -> Option<bool> {
fn validate_arg_conflicts(&self, a: &Arg<'a, 'b>, matcher: &ArgMatcher<'a>) -> bool {
debugln!("Validator::validate_arg_conflicts: a={:?};", a.name);
a.blacklist.as_ref().map(|bl| {
bl.iter().any(|ref conf| {
matcher.contains(conf)
|| find!(self.0.app, *conf, groups)
.map_or(false, |g| g.args.iter().any(|arg| matcher.contains(arg)))
a.blacklist
.as_ref()
.map(|bl| {
bl.iter().any(|ref conf| {
matcher.contains(conf)
|| self
.p
.app
.groups
.iter()
.find(|g| &g.name == *conf)
.map_or(false, |g| g.args.iter().any(|arg| matcher.contains(arg)))
})
})
})
.unwrap_or(false)
}
fn validate_required_unless(&self, a: &Arg<'a, 'b>, matcher: &ArgMatcher<'a>) -> Option<bool> {
debugln!("Validator::validate_required_unless: a={:?};", a.name);
fn validate_required_unless(&self, matcher: &ArgMatcher<'a>) -> ClapResult<()> {
debugln!("Validator::validate_required_unless;");
for a in self
.p
.app
.args
.iter()
.filter(|a| a.r_unless.is_some())
.filter(|a| !matcher.contains(a.name))
{
debugln!("Validator::validate_required_unless:iter:{};", a.name);
if self.fails_arg_required_unless(a, matcher) {
return self.missing_required_error(matcher, Some(a.name));
}
}
Ok(())
}
// Failing a required unless means, the arg's "unless" wasn't present, and neither were they
fn fails_arg_required_unless(&self, a: &Arg<'a, 'b>, matcher: &ArgMatcher<'a>) -> bool {
debugln!("Validator::fails_arg_required_unless: a={:?};", a.name);
macro_rules! check {
($how:ident, $_self:expr, $a:ident, $m:ident) => {{
$a.r_unless.as_ref().map(|ru| {
ru.iter().$how(|n| {
$m.contains(n) || {
if let Some(grp) = find!($_self.app, n, groups) {
grp.args.iter().any(|arg| $m.contains(arg))
} else {
false
}
}
$a.r_unless
.as_ref()
.map(|ru| {
!ru.iter().$how(|n| {
$m.contains(n)
// || {
// if let Some(grp) = $_self.app.groups.iter().find(|g| &g.name == n) {
// grp.args.iter().any(|arg| $m.contains(arg))
// } else {
// false
// }
// }
})
})
})
.unwrap_or(false)
}};
}
if a.is_set(ArgSettings::RequiredUnlessAll) {
check!(all, self.0, a, matcher)
debugln!("Validator::fails_arg_required_unless:{}:All;", a.name);
check!(all, self.p, a, matcher)
} else {
check!(any, self.0, a, matcher)
debugln!("Validator::fails_arg_required_unless:{}:Any;", a.name);
check!(any, self.p, a, matcher)
}
}
// `incl`: an arg to include in the error even if not used
fn missing_required_error(
&self,
matcher: &ArgMatcher<'a>,
extra: Option<&str>,
incl: Option<&str>,
) -> ClapResult<()> {
debugln!("Validator::missing_required_error: extra={:?}", extra);
debugln!("Validator::missing_required_error; incl={:?}", incl);
let c = Colorizer::new(ColorizerOption {
use_stderr: true,
when: self.0.app.color(),
when: self.p.app.color(),
});
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);
let req_args = Usage::new(self.0)
.get_required_usage_from(&reqs[..], Some(matcher), extra, true)
.iter()
.fold(String::new(), |acc, s| {
acc + &format!("\n {}", c.error(s))[..]
});
debugln!(
"Validator::missing_required_error: reqs={:?}",
self.p.required
);
let mut usg = Usage::new(self.p);
let req_args = if let Some(x) = incl {
usg.get_required_usage_from(&[x], Some(matcher), true)
.iter()
.fold(String::new(), |acc, s| {
acc + &format!("\n {}", c.error(s))[..]
})
} else {
usg.get_required_usage_from(&[], None, true)
.iter()
.fold(String::new(), |acc, s| {
acc + &format!("\n {}", c.error(s))[..]
})
};
debugln!(
"Validator::missing_required_error: req_args={:#?}",
req_args
);
let used: Vec<&str> = matcher
.arg_names()
.filter(|ref n| {
if let Some(a) = self.p.app.find(**n) {
!(self.p.required.contains(a.name) || a.is_set(ArgSettings::Hidden))
} else {
true
}
})
.map(|&n| n)
.chain(incl)
.collect();
Err(Error::missing_required_argument(
&*req_args,
&*Usage::new(self.0).create_error_usage(matcher, extra),
self.0.app.color(),
&*usg.create_usage_with_title(&*used),
self.p.app.color(),
))
}
}

60
src/util/graph.rs Normal file
View file

@ -0,0 +1,60 @@
#[derive(Debug)]
struct Child<T> {
id: T,
children: Option<Vec<usize>>,
}
impl<T> Child<T> {
fn new(id: T) -> Self {
Child {
id: id,
children: None,
}
}
}
#[derive(Debug)]
pub struct ChildGraph<T>(Vec<Child<T>>);
impl<T> ChildGraph<T>
where
T: Sized + PartialEq + Copy + Clone,
{
pub fn new() -> Self { ChildGraph(Vec::new()) }
pub fn with_capacity(s: usize) -> Self { ChildGraph(Vec::with_capacity(s)) }
pub fn insert(&mut self, req: T) -> usize {
if !self.contains(req) {
let idx = self.0.len();
self.0.push(Child::new(req));
idx
} else {
self.0
.iter()
.enumerate()
.find(|(i, e)| e.id == req)
.map(|(i, _)| i)
.unwrap()
}
}
pub fn insert_child(&mut self, parent: usize, child: T) -> usize {
let c_idx = self.0.len();
self.0.push(Child::new(child));
let parent = &mut self.0[parent];
if let Some(ref mut v) = parent.children {
v.push(c_idx);
} else {
let mut v = Vec::with_capacity(5);
v.push(c_idx);
parent.children = Some(v);
}
c_idx
}
pub fn iter(&self) -> impl Iterator<Item = &T> { self.0.iter().map(|r| &r.id) }
pub fn contains(&self, req: T) -> bool { self.0.iter().find(|r| r.id == req).is_some() }
}

View file

@ -1,9 +1,11 @@
mod graph;
mod map;
mod osstringext;
mod strext;
pub use self::graph::ChildGraph;
pub use self::map::{Values, VecMap};
pub use self::osstringext::OsStrExt2;
#[cfg(any(target_os = "windows", target_arch = "wasm32"))]
pub use self::osstringext::OsStrExt3;
pub use self::strext::_StrExt;
pub use self::strext::_StrExt;

View file

@ -18,7 +18,7 @@ static MISSING_REQ: &'static str = "error: The following required arguments were
--long-option-2 <option2>
USAGE:
clap-test <positional2> -F --long-option-2 <option2>
clap-test <positional2> --long-option-2 <option2> -F
For more information try --help";
@ -27,7 +27,7 @@ static COND_REQ_IN_USAGE: &'static str =
--output <output>
USAGE:
test --input <input> --output <output> --target <target>
test --target <target> --input <input> --output <output>
For more information try --help";
@ -155,7 +155,7 @@ fn arg_require_group() {
.group(ArgGroup::with_name("gr").arg("some").arg("other"))
.arg(Arg::from("--some 'some arg'"))
.arg(Arg::from("--other 'other arg'"))
.get_matches_from_safe(vec!["", "-f"]);
.try_get_matches_from(vec!["", "-f"]);
assert!(result.is_err());
let err = result.err().unwrap();
assert_eq!(err.kind, ErrorKind::MissingRequiredArgument);
@ -163,12 +163,14 @@ fn arg_require_group() {
#[test]
fn arg_require_group_2() {
let m = App::new("arg_require_group")
let res = App::new("arg_require_group")
.arg(Arg::from("-f, --flag 'some flag'").requires("gr"))
.group(ArgGroup::with_name("gr").arg("some").arg("other"))
.arg(Arg::from("--some 'some arg'"))
.arg(Arg::from("--other 'other arg'"))
.get_matches_from(vec!["", "-f", "--some"]);
.try_get_matches_from(vec!["", "-f", "--some"]);
assert!(res.is_ok());
let m = res.unwrap();
assert!(m.is_present("some"));
assert!(!m.is_present("other"));
assert!(m.is_present("flag"));
@ -176,12 +178,14 @@ fn arg_require_group_2() {
#[test]
fn arg_require_group_3() {
let m = App::new("arg_require_group")
let res = App::new("arg_require_group")
.arg(Arg::from("-f, --flag 'some flag'").requires("gr"))
.group(ArgGroup::with_name("gr").arg("some").arg("other"))
.arg(Arg::from("--some 'some arg'"))
.arg(Arg::from("--other 'other arg'"))
.get_matches_from(vec!["", "-f", "--other"]);
.try_get_matches_from(vec!["", "-f", "--other"]);
assert!(res.is_ok());
let m = res.unwrap();
assert!(!m.is_present("some"));
assert!(m.is_present("other"));
assert!(m.is_present("flag"));
@ -196,19 +200,15 @@ fn issue_753() {
"-l, --list 'List available interfaces (and stop there)'",
))
.arg(
Arg::from(
"-i, --iface=[INTERFACE] 'Ethernet interface for fetching NTP packets'",
).required_unless("list"),
Arg::from("-i, --iface=[INTERFACE] 'Ethernet interface for fetching NTP packets'")
.required_unless("list"),
)
.arg(
Arg::from("-f, --file=[TESTFILE] 'Fetch NTP packets from pcap file'")
.conflicts_with("iface")
.required_unless("list"),
)
.arg(
Arg::from("-s, --server=[SERVER_IP] 'NTP server IP address'")
.required_unless("list"),
)
.arg(Arg::from("-s, --server=[SERVER_IP] 'NTP server IP address'").required_unless("list"))
.arg(Arg::from("-p, --port=[SERVER_PORT] 'NTP server port'").default_value("123"))
.get_matches_from_safe(vec!["test", "--list"]);
assert!(m.is_ok());
@ -662,3 +662,45 @@ fn require_eq() {
);
assert!(test::compare_output(app, "clap-test", REQUIRE_EQUALS, true));
}
static ISSUE_1158: &'static str = "error: The following required arguments were not provided:
-x <X>
-y <Y>
-z <Z>
USAGE:
example <ID> -x <X> -y <Y> -z <Z>
For more information try --help";
fn issue_1158_app() -> App<'static, 'static> {
App::new("example")
.arg(
Arg::from_usage("-c, --config [FILE] 'Custom config file.'")
.required_unless("ID")
.conflicts_with("ID"),
)
.arg(
Arg::from_usage("[ID] 'ID'")
.required_unless("config")
.conflicts_with("config")
.requires_all(&["x", "y", "z"]),
)
.arg(Arg::from_usage("-x [X] 'X'"))
.arg(Arg::from_usage("-y [Y] 'Y'"))
.arg(Arg::from_usage("-z [Z] 'Z'"))
}
#[test]
fn issue_1158_conflicting_requirements() {
let app = issue_1158_app();
assert!(test::compare_output(app, "example id", ISSUE_1158, true));
}
#[test]
fn issue_1158_conflicting_requirements_rev() {
let res = issue_1158_app().try_get_matches_from(&["", "--config", "some.conf"]);
assert!(res.is_ok());
}