Skip to content

Commit

Permalink
Default values trigger conflicts no more (#1605)
Browse files Browse the repository at this point in the history
  • Loading branch information
CreepySkeleton committed Apr 26, 2020
1 parent f01dccb commit c9e03e7
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 26 deletions.
8 changes: 5 additions & 3 deletions src/parse/arg_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::ops::Deref;

// Internal
use crate::build::{Arg, ArgSettings};
use crate::parse::{ArgMatches, MatchedArg, SubCommand};
use crate::parse::{ArgMatches, MatchedArg, SubCommand, ValueType};
use crate::util::Id;

#[derive(Debug)]
Expand Down Expand Up @@ -136,20 +136,22 @@ impl ArgMatcher {
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 {
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),
vals: Vec::with_capacity(1),
});
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 {
occurs: 0,
indices: Vec::with_capacity(1),
vals: Vec::new(),
ty,
});
ma.indices.push(idx);
}
Expand Down
10 changes: 10 additions & 0 deletions src/parse/matches/matched_arg.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,18 @@
// Std
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)]
pub(crate) struct MatchedArg {
pub(crate) occurs: u64,
pub(crate) ty: ValueType,
pub(crate) indices: Vec<usize>,
pub(crate) vals: Vec<OsString>,
}
Expand All @@ -12,6 +21,7 @@ impl Default for MatchedArg {
fn default() -> Self {
MatchedArg {
occurs: 1,
ty: ValueType::CommandLine,
indices: Vec::new(),
vals: Vec::new(),
}
Expand Down
2 changes: 1 addition & 1 deletion src/parse/matches/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ mod arg_matches;
mod matched_arg;
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::subcommand::SubCommand;
6 changes: 5 additions & 1 deletion src/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@ mod parser;
mod validator;

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::{OsValues, SubCommand, Values};
47 changes: 27 additions & 20 deletions src/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use crate::parse::errors::Error as ClapError;
use crate::parse::errors::ErrorKind;
use crate::parse::errors::Result as ClapResult;
use crate::parse::features::suggestions;
use crate::parse::Validator;
use crate::parse::{ArgMatcher, SubCommand};
use crate::parse::{Validator, ValueType};
use crate::util::{termcolor::ColorChoice, ArgStr, ChildGraph, Id};
use crate::INTERNAL_ERROR_MSG;
use crate::INVALID_UTF8;
Expand Down Expand Up @@ -496,7 +496,12 @@ where
// Check to see if parsing a value from a previous arg

// 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
continue;
}
Expand Down Expand Up @@ -622,7 +627,7 @@ where
}

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);
for grp in groups_for_arg!(self.app, &p.id) {
Expand Down Expand Up @@ -660,7 +665,7 @@ where
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 {
Expand Down Expand Up @@ -1264,7 +1269,7 @@ where
fv,
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) {
debug!("None, but requires equals...Error");
return Err(ClapError::empty_value(
Expand Down Expand Up @@ -1301,6 +1306,7 @@ where
arg: &Arg<'b>,
val: &ArgStr<'_>,
matcher: &mut ArgMatcher,
ty: ValueType,
) -> ClapResult<ParseResult> {
debug!("Parser::add_val_to_arg; arg={}, val={:?}", arg.name, val);
debug!(
Expand All @@ -1311,11 +1317,11 @@ where
if !(self.is_set(AS::TrailingValues) && self.is_set(AS::DontDelimitTrailingValues)) {
if let Some(delim) = arg.val_delim {
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 {
let mut iret = ParseResult::ValuesDone(arg.id.clone());
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 val.contains_char(delim) || arg.is_set(ArgSettings::RequireDelimiter) {
Expand All @@ -1324,10 +1330,10 @@ where
Ok(iret)
}
} else {
self.add_single_val_to_arg(arg, val, matcher)
self.add_single_val_to_arg(arg, val, matcher, ty)
}
} else {
self.add_single_val_to_arg(arg, val, matcher)
self.add_single_val_to_arg(arg, val, matcher, ty)
}
}

Expand All @@ -1336,6 +1342,7 @@ where
arg: &Arg<'b>,
v: &ArgStr<'_>,
matcher: &mut ArgMatcher,
ty: ValueType,
) -> ClapResult<ParseResult> {
debug!("Parser::add_single_val_to_arg: adding val...{:?}", v);

Expand All @@ -1349,12 +1356,12 @@ where
}
}

matcher.add_val_to(&arg.id, v.to_os_string());
matcher.add_index_to(&arg.id, self.cur_idx.get());
matcher.add_val_to(&arg.id, v.to_os_string(), ty);
matcher.add_index_to(&arg.id, self.cur_idx.get(), ty);

// Increment or create the group "args"
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) {
Expand All @@ -1367,7 +1374,7 @@ where
debug!("Parser::parse_flag");

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"
for grp in groups_for_arg!(self.app, &flag.id) {
matcher.inc_occurrence_of(&grp);
Expand Down Expand Up @@ -1453,18 +1460,18 @@ where

for o in opts!(self.app) {
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) {
debug!("Parser::add_defaults:iter:{}:", p.name);
self.add_value(p, matcher)?;
self.add_value(p, matcher, ValueType::DefaultValue)?;
}

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 {
debug!("Parser::add_value: has conditional defaults");

Expand All @@ -1482,7 +1489,7 @@ where
};

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;
break;
}
Expand Down Expand Up @@ -1510,7 +1517,7 @@ where
);

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() {
debug!("Parser::add_value:iter:{}: has user defined vals", arg.name);
Expand All @@ -1520,7 +1527,7 @@ where
debug!("Parser::add_value:iter:{}: wasn't used", arg.name);

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 {
Expand All @@ -1541,7 +1548,7 @@ where
if matcher.get(&a.id).map_or(true, |a| a.occurs == 0) {
if let Some(ref val) = a.env {
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)?;
}
}
}
Expand Down
13 changes: 12 additions & 1 deletion src/parse/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::build::{Arg, ArgSettings};
use crate::output::Usage;
use crate::parse::errors::Result as ClapResult;
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::INTERNAL_ERROR_MSG;
use crate::INVALID_UTF8;
Expand Down Expand Up @@ -277,6 +277,17 @@ impl<'b, 'c, 'z> Validator<'b, 'c, 'z> {
debug!("Validator::gather_conflicts");
for name in matcher.arg_names() {
debug!("Validator::gather_conflicts:iter:{:?}", 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)
{
continue;
}

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 {
Expand Down

0 comments on commit c9e03e7

Please sign in to comment.