1869:  Default values trigger conflicts no more r=pksunkara a=CreepySkeleton



Co-authored-by: CreepySkeleton <creepy-skeleton@yandex.ru>
This commit is contained in:
bors[bot] 2020-04-26 18:34:59 +00:00 committed by GitHub
commit 1e800ed2b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 82 additions and 29 deletions

View file

@ -6,7 +6,7 @@ use std::ops::Deref;
// Internal // Internal
use crate::build::{Arg, ArgSettings}; use crate::build::{Arg, ArgSettings};
use crate::parse::{ArgMatches, MatchedArg, SubCommand}; use crate::parse::{ArgMatches, MatchedArg, SubCommand, ValueType};
use crate::util::Id; use crate::util::Id;
#[derive(Debug)] #[derive(Debug)]
@ -136,20 +136,22 @@ impl ArgMatcher {
self.insert(arg); self.insert(arg);
} }
pub(crate) fn add_val_to(&mut self, arg: &Id, val: OsString) { pub(crate) fn add_val_to(&mut self, arg: &Id, val: OsString, ty: ValueType) {
let ma = self.entry(arg).or_insert(MatchedArg { let ma = self.entry(arg).or_insert(MatchedArg {
occurs: 0, // @TODO @question Shouldn't this be 1 if we're already adding a value to this arg? occurs: 0, // @TODO @question Shouldn't this be 1 if we're already adding a value to this arg?
ty,
indices: Vec::with_capacity(1), indices: Vec::with_capacity(1),
vals: Vec::with_capacity(1), vals: Vec::with_capacity(1),
}); });
ma.vals.push(val); ma.vals.push(val);
} }
pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize) { pub(crate) fn add_index_to(&mut self, arg: &Id, idx: usize, ty: ValueType) {
let ma = self.entry(arg).or_insert(MatchedArg { let ma = self.entry(arg).or_insert(MatchedArg {
occurs: 0, occurs: 0,
indices: Vec::with_capacity(1), indices: Vec::with_capacity(1),
vals: Vec::new(), vals: Vec::new(),
ty,
}); });
ma.indices.push(idx); ma.indices.push(idx);
} }

View file

@ -1,9 +1,18 @@
// Std // Std
use std::ffi::{OsStr, OsString}; use std::ffi::{OsStr, OsString};
// TODO: Maybe make this public?
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum ValueType {
EnvVariable,
CommandLine,
DefaultValue,
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct MatchedArg { pub(crate) struct MatchedArg {
pub(crate) occurs: u64, pub(crate) occurs: u64,
pub(crate) ty: ValueType,
pub(crate) indices: Vec<usize>, pub(crate) indices: Vec<usize>,
pub(crate) vals: Vec<OsString>, pub(crate) vals: Vec<OsString>,
} }
@ -12,6 +21,7 @@ impl Default for MatchedArg {
fn default() -> Self { fn default() -> Self {
MatchedArg { MatchedArg {
occurs: 1, occurs: 1,
ty: ValueType::CommandLine,
indices: Vec::new(), indices: Vec::new(),
vals: Vec::new(), vals: Vec::new(),
} }

View file

@ -2,7 +2,7 @@ mod arg_matches;
mod matched_arg; mod matched_arg;
pub mod subcommand; pub mod subcommand;
pub(crate) use self::matched_arg::MatchedArg; pub(crate) use self::matched_arg::{MatchedArg, ValueType};
pub use self::arg_matches::{ArgMatches, OsValues, Values}; pub use self::arg_matches::{ArgMatches, OsValues, Values};
pub use self::subcommand::SubCommand; pub use self::subcommand::SubCommand;

View file

@ -7,7 +7,11 @@ mod parser;
mod validator; mod validator;
pub(crate) use self::parser::{Input, ParseResult, Parser}; pub(crate) use self::parser::{Input, ParseResult, Parser};
pub(crate) use self::{arg_matcher::ArgMatcher, matches::MatchedArg, validator::Validator}; pub(crate) use self::{
arg_matcher::ArgMatcher,
matches::{MatchedArg, ValueType},
validator::Validator,
};
pub use self::matches::ArgMatches; pub use self::matches::ArgMatches;
pub use self::matches::{OsValues, SubCommand, Values}; pub use self::matches::{OsValues, SubCommand, Values};

View file

@ -13,8 +13,8 @@ use crate::parse::errors::Error as ClapError;
use crate::parse::errors::ErrorKind; use crate::parse::errors::ErrorKind;
use crate::parse::errors::Result as ClapResult; use crate::parse::errors::Result as ClapResult;
use crate::parse::features::suggestions; use crate::parse::features::suggestions;
use crate::parse::Validator;
use crate::parse::{ArgMatcher, SubCommand}; use crate::parse::{ArgMatcher, SubCommand};
use crate::parse::{Validator, ValueType};
use crate::util::{termcolor::ColorChoice, ArgStr, ChildGraph, Id}; use crate::util::{termcolor::ColorChoice, ArgStr, ChildGraph, Id};
use crate::INTERNAL_ERROR_MSG; use crate::INTERNAL_ERROR_MSG;
use crate::INVALID_UTF8; use crate::INVALID_UTF8;
@ -496,7 +496,12 @@ where
// Check to see if parsing a value from a previous arg // Check to see if parsing a value from a previous arg
// get the option so we can check the settings // get the option so we can check the settings
needs_val_of = self.add_val_to_arg(&self.app[&id], &arg_os, matcher)?; needs_val_of = self.add_val_to_arg(
&self.app[&id],
&arg_os,
matcher,
ValueType::CommandLine,
)?;
// get the next value from the iterator // get the next value from the iterator
continue; continue;
} }
@ -622,7 +627,7 @@ where
} }
self.seen.push(p.id.clone()); self.seen.push(p.id.clone());
self.add_val_to_arg(p, &arg_os, matcher)?; self.add_val_to_arg(p, &arg_os, matcher, ValueType::CommandLine)?;
matcher.inc_occurrence_of(&p.id); matcher.inc_occurrence_of(&p.id);
for grp in groups_for_arg!(self.app, &p.id) { for grp in groups_for_arg!(self.app, &p.id) {
@ -660,7 +665,7 @@ where
self.app.color(), self.app.color(),
)?); )?);
} }
sc_m.add_val_to(&Id::empty_hash(), v.to_os_string()); sc_m.add_val_to(&Id::empty_hash(), v.to_os_string(), ValueType::CommandLine);
} }
matcher.subcommand(SubCommand { matcher.subcommand(SubCommand {
@ -1264,7 +1269,7 @@ where
fv, fv,
fv.starts_with("=") fv.starts_with("=")
); );
self.add_val_to_arg(opt, &v, matcher)?; self.add_val_to_arg(opt, &v, matcher, ValueType::CommandLine)?;
} else if needs_eq && !(empty_vals || min_vals_zero) { } else if needs_eq && !(empty_vals || min_vals_zero) {
debug!("None, but requires equals...Error"); debug!("None, but requires equals...Error");
return Err(ClapError::empty_value( return Err(ClapError::empty_value(
@ -1301,6 +1306,7 @@ where
arg: &Arg<'b>, arg: &Arg<'b>,
val: &ArgStr<'_>, val: &ArgStr<'_>,
matcher: &mut ArgMatcher, matcher: &mut ArgMatcher,
ty: ValueType,
) -> ClapResult<ParseResult> { ) -> ClapResult<ParseResult> {
debug!("Parser::add_val_to_arg; arg={}, val={:?}", arg.name, val); debug!("Parser::add_val_to_arg; arg={}, val={:?}", arg.name, val);
debug!( debug!(
@ -1311,11 +1317,11 @@ where
if !(self.is_set(AS::TrailingValues) && self.is_set(AS::DontDelimitTrailingValues)) { if !(self.is_set(AS::TrailingValues) && self.is_set(AS::DontDelimitTrailingValues)) {
if let Some(delim) = arg.val_delim { if let Some(delim) = arg.val_delim {
if val.is_empty() { if val.is_empty() {
Ok(self.add_single_val_to_arg(arg, val, matcher)?) Ok(self.add_single_val_to_arg(arg, val, matcher, ty)?)
} else { } else {
let mut iret = ParseResult::ValuesDone(arg.id.clone()); let mut iret = ParseResult::ValuesDone(arg.id.clone());
for v in val.split(delim) { for v in val.split(delim) {
iret = self.add_single_val_to_arg(arg, &v, matcher)?; iret = self.add_single_val_to_arg(arg, &v, matcher, ty)?;
} }
// If there was a delimiter used, we're not looking for more values // If there was a delimiter used, we're not looking for more values
if val.contains_char(delim) || arg.is_set(ArgSettings::RequireDelimiter) { if val.contains_char(delim) || arg.is_set(ArgSettings::RequireDelimiter) {
@ -1324,10 +1330,10 @@ where
Ok(iret) Ok(iret)
} }
} else { } else {
self.add_single_val_to_arg(arg, val, matcher) self.add_single_val_to_arg(arg, val, matcher, ty)
} }
} else { } else {
self.add_single_val_to_arg(arg, val, matcher) self.add_single_val_to_arg(arg, val, matcher, ty)
} }
} }
@ -1336,6 +1342,7 @@ where
arg: &Arg<'b>, arg: &Arg<'b>,
v: &ArgStr<'_>, v: &ArgStr<'_>,
matcher: &mut ArgMatcher, matcher: &mut ArgMatcher,
ty: ValueType,
) -> ClapResult<ParseResult> { ) -> ClapResult<ParseResult> {
debug!("Parser::add_single_val_to_arg: adding val...{:?}", v); debug!("Parser::add_single_val_to_arg: adding val...{:?}", v);
@ -1349,12 +1356,12 @@ where
} }
} }
matcher.add_val_to(&arg.id, v.to_os_string()); matcher.add_val_to(&arg.id, v.to_os_string(), ty);
matcher.add_index_to(&arg.id, self.cur_idx.get()); matcher.add_index_to(&arg.id, self.cur_idx.get(), ty);
// Increment or create the group "args" // Increment or create the group "args"
for grp in groups_for_arg!(self.app, &arg.id) { for grp in groups_for_arg!(self.app, &arg.id) {
matcher.add_val_to(&grp, v.to_os_string()); matcher.add_val_to(&grp, v.to_os_string(), ty);
} }
if matcher.needs_more_vals(arg) { if matcher.needs_more_vals(arg) {
@ -1367,7 +1374,7 @@ where
debug!("Parser::parse_flag"); debug!("Parser::parse_flag");
matcher.inc_occurrence_of(&flag.id); matcher.inc_occurrence_of(&flag.id);
matcher.add_index_to(&flag.id, self.cur_idx.get()); matcher.add_index_to(&flag.id, self.cur_idx.get(), ValueType::CommandLine);
// Increment or create the group "args" // Increment or create the group "args"
for grp in groups_for_arg!(self.app, &flag.id) { for grp in groups_for_arg!(self.app, &flag.id) {
matcher.inc_occurrence_of(&grp); matcher.inc_occurrence_of(&grp);
@ -1453,18 +1460,18 @@ where
for o in opts!(self.app) { for o in opts!(self.app) {
debug!("Parser::add_defaults:iter:{}:", o.name); debug!("Parser::add_defaults:iter:{}:", o.name);
self.add_value(o, matcher)?; self.add_value(o, matcher, ValueType::DefaultValue)?;
} }
for p in positionals!(self.app) { for p in positionals!(self.app) {
debug!("Parser::add_defaults:iter:{}:", p.name); debug!("Parser::add_defaults:iter:{}:", p.name);
self.add_value(p, matcher)?; self.add_value(p, matcher, ValueType::DefaultValue)?;
} }
Ok(()) Ok(())
} }
fn add_value(&self, arg: &Arg<'b>, matcher: &mut ArgMatcher) -> ClapResult<()> { fn add_value(&self, arg: &Arg<'b>, matcher: &mut ArgMatcher, ty: ValueType) -> ClapResult<()> {
if let Some(ref vm) = arg.default_vals_ifs { if let Some(ref vm) = arg.default_vals_ifs {
debug!("Parser::add_value: has conditional defaults"); debug!("Parser::add_value: has conditional defaults");
@ -1482,7 +1489,7 @@ where
}; };
if add { if add {
self.add_val_to_arg(arg, &ArgStr::new(default), matcher)?; self.add_val_to_arg(arg, &ArgStr::new(default), matcher, ty)?;
done = true; done = true;
break; break;
} }
@ -1510,7 +1517,7 @@ where
); );
for val in vals { for val in vals {
self.add_val_to_arg(arg, &ArgStr::new(val), matcher)?; self.add_val_to_arg(arg, &ArgStr::new(val), matcher, ty)?;
} }
} else if matcher.get(&arg.id).is_some() { } else if matcher.get(&arg.id).is_some() {
debug!("Parser::add_value:iter:{}: has user defined vals", arg.name); debug!("Parser::add_value:iter:{}: has user defined vals", arg.name);
@ -1520,7 +1527,7 @@ where
debug!("Parser::add_value:iter:{}: wasn't used", arg.name); debug!("Parser::add_value:iter:{}: wasn't used", arg.name);
for val in vals { for val in vals {
self.add_val_to_arg(arg, &ArgStr::new(val), matcher)?; self.add_val_to_arg(arg, &ArgStr::new(val), matcher, ty)?;
} }
} }
} else { } else {
@ -1541,7 +1548,7 @@ where
if matcher.get(&a.id).map_or(true, |a| a.occurs == 0) { if matcher.get(&a.id).map_or(true, |a| a.occurs == 0) {
if let Some(ref val) = a.env { if let Some(ref val) = a.env {
if let Some(ref val) = val.1 { if let Some(ref val) = val.1 {
self.add_val_to_arg(a, &ArgStr::new(val), matcher)?; self.add_val_to_arg(a, &ArgStr::new(val), matcher, ValueType::EnvVariable)?;
} }
} }
} }

View file

@ -4,7 +4,7 @@ use crate::build::{Arg, ArgSettings};
use crate::output::Usage; use crate::output::Usage;
use crate::parse::errors::Result as ClapResult; use crate::parse::errors::Result as ClapResult;
use crate::parse::errors::{Error, ErrorKind}; use crate::parse::errors::{Error, ErrorKind};
use crate::parse::{ArgMatcher, MatchedArg, ParseResult, Parser}; use crate::parse::{ArgMatcher, MatchedArg, ParseResult, Parser, ValueType};
use crate::util::{ChildGraph, Id}; use crate::util::{ChildGraph, Id};
use crate::INTERNAL_ERROR_MSG; use crate::INTERNAL_ERROR_MSG;
use crate::INVALID_UTF8; use crate::INVALID_UTF8;
@ -276,7 +276,19 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
fn gather_conflicts(&mut self, matcher: &mut ArgMatcher) { fn gather_conflicts(&mut self, matcher: &mut ArgMatcher) {
debug!("Validator::gather_conflicts"); debug!("Validator::gather_conflicts");
for name in matcher.arg_names() { for name in matcher.arg_names() {
debug!("Validator::gather_conflicts:iter:{:?}", name); debug!("Validator::gather_conflicts:iter: id={:?}", name);
// if arg is "present" only because it got default value
// it doesn't conflict with anything
//
// TODO: @refactor Do it in a more elegant way
if matcher
.get(name)
.map_or(false, |a| a.ty == ValueType::DefaultValue)
{
debug!("Validator::gather_conflicts:iter: This is default value, skipping.",);
continue;
}
if let Some(arg) = self.p.app.find(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 // Since an arg was used, every arg it conflicts with is added to the conflicts
if let Some(ref bl) = arg.blacklist { if let Some(ref bl) = arg.blacklist {

View file

@ -28,9 +28,9 @@ impl BufferWriter {
pub(crate) fn print(&self, buf: &Buffer) -> Result<()> { pub(crate) fn print(&self, buf: &Buffer) -> Result<()> {
if self.use_stderr { if self.use_stderr {
stderr().lock().write(buf)?; stderr().lock().write_all(buf)?;
} else { } else {
stdout().lock().write(buf)?; stdout().lock().write_all(buf)?;
} }
Ok(()) Ok(())

View file

@ -204,3 +204,21 @@ fn conflicts_with_invalid_arg() {
) )
.try_get_matches_from(vec!["", "--config"]); .try_get_matches_from(vec!["", "--config"]);
} }
#[test]
fn conflicts_with_default() {
let result = App::new("conflict")
.arg(
Arg::from("-o, --opt=[opt] 'some opt'")
.default_value("default")
.conflicts_with("flag"),
)
.arg(Arg::from("-f, --flag 'some flag'").conflicts_with("opt"))
.try_get_matches_from(vec!["myprog", "-f"]);
assert!(result.is_ok(), "{:?}", result.unwrap());
let m = result.unwrap();
assert_eq!(m.value_of("opt"), Some("default"));
assert!(m.is_present("flag"));
}