clap/src/app/macros.rs
Kevin K 3d37001d1d
imp(Error Output): conflicting errors are now symetrical, meaning more consistent and less confusing
Prior to this commit, conflicting error messages and the suggeseted usage would depend on whether
you defined the conflict on both arguments, or just one, and the order in which you specified the
conflicting arguments at runtime.

Now they are symetrical, meaning the suggestions from the error message are consistent, and it no
longer matters if you specify the conflict in one, or both arguments.

Closes #718
2016-10-31 00:35:23 -04:00

231 lines
No EOL
8.2 KiB
Rust

macro_rules! remove_overriden {
(@remove $_self:ident, $v:ident, $a:ident.$ov:ident) => {
if let Some(ref ora) = $a.$ov {
vec_remove_all!($_self.$v, ora);
}
};
(@arg $_self:ident, $arg:ident) => {
remove_overriden!(@remove $_self, required, $arg.requires);
remove_overriden!(@remove $_self, blacklist, $arg.blacklist);
remove_overriden!(@remove $_self, overrides, $arg.overrides);
};
($_self:ident, $name:expr) => {
debugln!("macro=remove_overriden!;");
if let Some(ref o) = $_self.opts.iter().filter(|o| o.name == *$name).next() {
remove_overriden!(@arg $_self, o);
} else if let Some(ref f) = $_self.flags.iter().filter(|f| f.name == *$name).next() {
remove_overriden!(@arg $_self, f);
} else if let Some(p) = $_self.positionals.values().filter(|p| p.name == *$name).next() {
remove_overriden!(@arg $_self, p);
}
};
}
macro_rules! arg_post_processing {
($me:ident, $arg:ident, $matcher:ident) => {
debugln!("macro=arg_post_processing!;");
// Handle POSIX overrides
debug!("Is '{}' in overrides...", $arg.to_string());
if $me.overrides.contains(&$arg.name()) {
if let Some(ref name) = find_name_from!($me, &$arg.name(), overrides, $matcher) {
sdebugln!("Yes by {}", name);
$matcher.remove(name);
remove_overriden!($me, name);
}
} else { sdebugln!("No"); }
// Add overrides
debug!("Does '{}' have overrides...", $arg.to_string());
if let Some(or) = $arg.overrides() {
sdebugln!("Yes");
$matcher.remove_all(or);
for pa in or { remove_overriden!($me, pa); }
$me.overrides.extend(or);
vec_remove_all!($me.required, or);
} else { sdebugln!("No"); }
// Handle conflicts
debug!("Does '{}' have conflicts...", $arg.to_string());
if let Some(bl) = $arg.blacklist() {
sdebugln!("Yes");
for c in bl {
// Inject two-way conflicts
debug!("Has '{}' already been matched...", c);
if $matcher.contains(c) {
sdebugln!("Yes");
// find who blacklisted us...
$me.blacklist.push(&$arg.name);
// if let Some(f) = $me.find_flag_mut(c) {
// if let Some(ref mut bl) = f.blacklist {
// bl.push(&$arg.name);
// }
// } else if let Some(o) = $me.find_option_mut(c) {
// if let Some(ref mut bl) = o.blacklist {
// bl.push(&$arg.name);
// }
// } else if let Some(p) = $me.find_positional_mut(c) {
// if let Some(ref mut bl) = p.blacklist {
// bl.push(&$arg.name);
// }
// }
} else {
sdebugln!("No");
}
}
$me.blacklist.extend(bl);
vec_remove_all!($me.overrides, bl);
vec_remove_all!($me.required, bl);
} else { sdebugln!("No"); }
// Add all required args which aren't already found in matcher to the master
// list
debug!("Does '{}' have requirements...", $arg.to_string());
if let Some(reqs) = $arg.requires() {
for n in reqs {
if $matcher.contains(&n) {
sdebugln!("\tYes '{}' but it's already met", n);
continue;
} else { sdebugln!("\tYes '{}'", n); }
$me.required.push(n);
}
} else { sdebugln!("No"); }
_handle_group_reqs!($me, $arg);
};
}
macro_rules! _handle_group_reqs{
($me:ident, $arg:ident) => ({
use args::AnyArg;
debugln!("macro=_handle_group_reqs!;");
for grp in $me.groups.values() {
let found = if grp.args.contains(&$arg.name()) {
vec_remove!($me.required, &$arg.name());
if let Some(ref reqs) = grp.requires {
$me.required.extend(reqs);
}
if let Some(ref bl) = grp.conflicts {
$me.blacklist.extend(bl);
}
true // What if arg is in more than one group with different reqs?
} else {
false
};
debugln!("iter;grp={};found={:?}", grp.name, found);
if found {
vec_remove_all!($me.required, &grp.args);
debugln!("Adding args from group to blacklist...{:?}", grp.args);
if !grp.multiple {
$me.blacklist.extend(&grp.args);
vec_remove!($me.blacklist, &$arg.name());
}
}
}
})
}
macro_rules! validate_multiples {
($_self:ident, $a:ident, $m:ident) => {
debugln!("macro=validate_multiples!;");
if $m.contains(&$a.name) && !$a.settings.is_set(ArgSettings::Multiple) {
// Not the first time, and we don't allow multiples
return Err(Error::unexpected_multiple_usage($a,
&*$_self.create_current_usage($m),
$_self.color()))
}
};
}
macro_rules! parse_positional {
(
$_self:ident,
$p:ident,
$arg_os:ident,
$pos_counter:ident,
$matcher:ident
) => {
debugln!("macro=parse_positional!;");
validate_multiples!($_self, $p, $matcher);
if !$_self.trailing_vals &&
($_self.settings.is_set(AppSettings::TrailingVarArg) &&
$pos_counter == $_self.positionals.len()) {
$_self.trailing_vals = true;
}
if let Err(e) = $_self.add_val_to_arg($p, &$arg_os, $matcher) {
return Err(e);
}
$matcher.inc_occurrence_of($p.name);
let _ = $_self.groups_for_arg($p.name)
.and_then(|vec| Some($matcher.inc_occurrences_of(&*vec)));
arg_post_processing!($_self, $p, $matcher);
// Only increment the positional counter if it doesn't allow multiples
if !$p.settings.is_set(ArgSettings::Multiple) {
$pos_counter += 1;
}
};
}
macro_rules! find_from {
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
let mut ret = None;
for k in $matcher.arg_names() {
if let Some(f) = $_self.find_flag(k) {
if let Some(ref v) = f.$from {
if v.contains($arg_name) {
ret = Some(f.to_string());
}
}
}
if let Some(o) = $_self.find_option(k) {
if let Some(ref v) = o.$from {
if v.contains(&$arg_name) {
ret = Some(o.to_string());
}
}
}
if let Some(pos) = $_self.find_positional(k) {
if let Some(ref v) = pos.$from {
if v.contains($arg_name) {
ret = Some(pos.name.to_owned());
}
}
}
}
ret
}};
}
macro_rules! find_name_from {
($_self:ident, $arg_name:expr, $from:ident, $matcher:expr) => {{
let mut ret = None;
for k in $matcher.arg_names() {
if let Some(f) = $_self.find_flag(k) {
if let Some(ref v) = f.$from {
if v.contains($arg_name) {
ret = Some(f.name);
}
}
}
if let Some(o) = $_self.find_option(k) {
if let Some(ref v) = o.$from {
if v.contains(&$arg_name) {
ret = Some(o.name);
}
}
}
if let Some(pos) = $_self.find_positional(k) {
if let Some(ref v) = pos.$from {
if v.contains($arg_name) {
ret = Some(pos.name);
}
}
}
}
ret
}};
}