mirror of
https://github.com/clap-rs/clap
synced 2024-11-10 23:04:23 +00:00
fix(Requirements): fixing requirements and conflicts for issue 1158
Fixes requirements and conflicts on the v3 branch Closes #1158
This commit is contained in:
parent
c22bc3e1fc
commit
5a06a8270a
11 changed files with 761 additions and 547 deletions
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) => {{
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() }
|
||||
|
|
|
@ -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
60
src/util/graph.rs
Normal 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() }
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue