Skip to content

Commit d715646

Browse files
committed
feat(Args): allows defining POSIX compatible argument conflicts
1 parent d0c3b37 commit d715646

File tree

2 files changed

+164
-48
lines changed

2 files changed

+164
-48
lines changed

src/app.rs

+108-48
Original file line numberDiff line numberDiff line change
@@ -2161,7 +2161,10 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
21612161
// Make sure this one doesn't conflict with anything
21622162
self.blacklist.dedup();
21632163
if self.blacklist.contains(&p.name) {
2164-
matches.args.remove(p.name);
2164+
// we shouldn't need to remove this arg...since it should be matched yet
2165+
// anyways
2166+
// matches.args.remove(p.name);
2167+
21652168
self.report_error(format!("The argument '{}' cannot be used with {}",
21662169
Format::Warning(p.to_string()),
21672170
match self.blacklisted_from(p.name, &matches) {
@@ -2173,6 +2176,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
21732176
Some(matches.args.keys().map(|k| *k).collect()));
21742177
}
21752178

2179+
21762180
if let Some(ref p_vals) = p.possible_vals {
21772181
if !p_vals.is_empty() {
21782182
if !p_vals.contains(arg_slice) {
@@ -2181,6 +2185,7 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
21812185
}
21822186
}
21832187
}
2188+
21842189
// Have we made the update yet?
21852190
let mut done = false;
21862191
if p.multiple {
@@ -2216,12 +2221,27 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
22162221
vals.insert(len, arg_slice.to_owned());
22172222
}
22182223
}
2224+
22192225
} else {
22202226
// Only increment the positional counter if it doesn't allow multiples
22212227
pos_counter += 1;
22222228
}
22232229
// Was an update made, or is this the first occurrence?
22242230
if !done {
2231+
self.overrides.dedup();
2232+
if self.overrides.contains(&p.name) {
2233+
if let Some(name) = self.overriden_from(p.name, matches) {
2234+
matches.args.remove(&*name);
2235+
remove_override!(self, &*name);
2236+
}
2237+
}
2238+
if let Some(ref or) = p.overrides {
2239+
for pa in or {
2240+
matches.args.remove(pa);
2241+
remove_override!(self, pa);
2242+
self.overrides.push(pa);
2243+
}
2244+
}
22252245
let mut bm = BTreeMap::new();
22262246
if !p.empty_vals && arg_slice.is_empty() {
22272247
self.report_error(format!("The argument '{}' does not allow empty \
@@ -2243,29 +2263,29 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
22432263
occurrences: 1,
22442264
values: Some(bm),
22452265
});
2246-
}
22472266

2248-
if let Some(ref bl) = p.blacklist {
2249-
for name in bl {
2250-
self.blacklist.push(name);
2251-
vec_remove!(self.required, name);
2267+
if let Some(ref bl) = p.blacklist {
2268+
for name in bl {
2269+
self.blacklist.push(name);
2270+
vec_remove!(self.required, name);
2271+
}
22522272
}
2253-
}
22542273

2255-
// Because of the macro call, we have to create a temp variable
2256-
let pname = &p.name;
2257-
vec_remove!(self.required, pname);
2258-
if let Some(ref reqs) = p.requires {
2259-
// Add all required args which aren't already found in matches to the
2260-
// final required list
2261-
for n in reqs {
2262-
if matches.args.contains_key(n) {continue;}
2274+
// Because of the macro call, we have to create a temp variable
2275+
let pname = &p.name;
2276+
vec_remove!(self.required, pname);
2277+
if let Some(ref reqs) = p.requires {
2278+
// Add all required args which aren't already found in matches to the
2279+
// final required list
2280+
for n in reqs {
2281+
if matches.args.contains_key(n) {continue;}
22632282

2264-
self.required.push(n);
2283+
self.required.push(n);
2284+
}
22652285
}
2266-
}
22672286

2268-
parse_group_reqs!(self, p);
2287+
parse_group_reqs!(self, p);
2288+
}
22692289

22702290
} else {
22712291
self.report_error(format!("The argument '{}' was found, but '{}' wasn't \
@@ -2323,7 +2343,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
23232343
self.validate_blacklist(matches);
23242344
self.validate_num_args(matches);
23252345

2326-
23272346
matches.usage = Some(self.create_usage(None));
23282347

23292348
if let Some(sc_name) = subcmd_name {
@@ -2422,6 +2441,35 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
24222441
None
24232442
}
24242443

2444+
fn overriden_from(&self, name: &'ar str, matches: &ArgMatches) -> Option<&'ar str> {
2445+
for k in matches.args.keys() {
2446+
if let Some(f) = self.flags.get(k) {
2447+
if let Some(ref bl) = f.overrides {
2448+
if bl.contains(&name) {
2449+
return Some(f.name)
2450+
}
2451+
}
2452+
}
2453+
if let Some(o) = self.opts.get(k) {
2454+
if let Some(ref bl) = o.overrides {
2455+
if bl.contains(&name) {
2456+
return Some(o.name)
2457+
}
2458+
}
2459+
}
2460+
if let Some(idx) = self.positionals_name.get(k) {
2461+
if let Some(pos) = self.positionals_idx.get(idx) {
2462+
if let Some(ref bl) = pos.overrides {
2463+
if bl.contains(&name) {
2464+
return Some(pos.name)
2465+
}
2466+
}
2467+
}
2468+
}
2469+
}
2470+
None
2471+
}
2472+
24252473
fn create_help_and_version(&mut self) {
24262474
// name is "hclap_help" because flags are sorted by name
24272475
if self.needs_long_help {
@@ -2522,8 +2570,12 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
25222570
true,
25232571
Some(matches.args.keys().map(|k| *k).collect()));
25242572
}
2525-
if self.overrides.contains(&v.name) {
2526-
matches.args.remove(v.name);
2573+
if let Some(ref or) = v.overrides {
2574+
for pa in or {
2575+
matches.args.remove(pa);
2576+
remove_override!(self, pa);
2577+
self.overrides.push(pa);
2578+
}
25272579
}
25282580

25292581
if matches.args.contains_key(v.name) {
@@ -2594,12 +2646,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
25942646
}
25952647
});
25962648
}
2597-
if let Some(ref ov) = v.overrides {
2598-
for name in ov {
2599-
self.overrides.push(name);
2600-
vec_remove!(self.required, name);
2601-
}
2602-
}
26032649
if let Some(ref bl) = v.blacklist {
26042650
for name in bl {
26052651
self.blacklist.push(name);
@@ -2645,9 +2691,12 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
26452691
true,
26462692
Some(matches.args.keys().map(|k| *k).collect()));
26472693
}
2648-
self.overrides.dedup();
2649-
if self.overrides.contains(&v.name) {
2650-
matches.args.remove(v.name);
2694+
if let Some(ref or) = v.overrides {
2695+
for pa in or {
2696+
matches.args.remove(pa);
2697+
remove_override!(self, pa);
2698+
self.overrides.push(pa);
2699+
}
26512700
}
26522701

26532702
// Make sure this isn't one being added multiple times if it doesn't suppor it
@@ -2793,7 +2842,17 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
27932842
}
27942843
self.overrides.dedup();
27952844
if self.overrides.contains(&v.name) {
2796-
matches.args.remove(v.name);
2845+
if let Some(name) = self.overriden_from(v.name, matches) {
2846+
matches.args.remove(&*name);
2847+
remove_override!(self, &*name);
2848+
}
2849+
}
2850+
if let Some(ref or) = v.overrides {
2851+
for pa in or {
2852+
matches.args.remove(pa);
2853+
remove_override!(self, pa);
2854+
self.overrides.push(pa);
2855+
}
27972856
}
27982857

27992858
if matches.args.contains_key(v.name) {
@@ -2811,12 +2870,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
28112870
values: Some(BTreeMap::new())
28122871
});
28132872
}
2814-
if let Some(ref ov) = v.overrides {
2815-
for name in ov {
2816-
self.overrides.push(name);
2817-
vec_remove!(self.required, name);
2818-
}
2819-
}
28202873
if let Some(ref bl) = v.blacklist {
28212874
for name in bl {
28222875
self.blacklist.push(name);
@@ -2853,9 +2906,9 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
28532906
}
28542907

28552908
fn parse_single_short_flag(&mut self, matches: &mut ArgMatches<'ar, 'ar>, arg: char) -> bool {
2856-
for v in self.flags.values()
2909+
if let Some(v) = self.flags.values()
28572910
.filter(|&v| v.short.is_some())
2858-
.filter(|&v| v.short.unwrap() == arg) {
2911+
.filter(|&v| v.short.unwrap() == arg).nth(0) {
28592912
// Ensure this flag isn't on the mutually excludes list
28602913
self.blacklist.dedup();
28612914
if self.blacklist.contains(&v.name) {
@@ -2871,8 +2924,22 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
28712924
Some(matches.args.keys().map(|k| *k).collect()));
28722925
}
28732926
self.overrides.dedup();
2927+
debugln!("checking if {} is in overrides", v.name);
28742928
if self.overrides.contains(&v.name) {
2875-
matches.args.remove(v.name);
2929+
debugln!("it is...");
2930+
debugln!("checking who defined it...");
2931+
if let Some(name) = self.overriden_from(v.name, matches) {
2932+
debugln!("found {}", name);
2933+
matches.args.remove(name);
2934+
remove_override!(self, name);
2935+
}
2936+
}
2937+
if let Some(ref or) = v.overrides {
2938+
for pa in or {
2939+
matches.args.remove(pa);
2940+
remove_override!(self, pa);
2941+
self.overrides.push(pa);
2942+
}
28762943
}
28772944

28782945
// Make sure this isn't one being added multiple times if it doesn't suppor it
@@ -2902,13 +2969,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
29022969
let vname = &v.name;
29032970
vec_remove!(self.required, vname);
29042971

2905-
// Add all of this flags "mutually excludes" list to the master list
2906-
if let Some(ref ov) = v.overrides {
2907-
for name in ov {
2908-
self.overrides.push(name);
2909-
vec_remove!(self.required, name);
2910-
}
2911-
}
29122972
if let Some(ref bl) = v.blacklist {
29132973
for name in bl {
29142974
self.blacklist.push(name);
@@ -3076,7 +3136,6 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
30763136
fn validate_required(&self, matches: &ArgMatches<'ar, 'ar>) -> bool{
30773137
for name in self.required.iter() {
30783138
validate_reqs!(self, flags, matches, name);
3079-
30803139
validate_reqs!(self, opts, matches, name);
30813140

30823141
// because positions use different keys, we dont use the macro
@@ -3126,4 +3185,5 @@ impl<'a, 'v, 'ab, 'u, 'h, 'ar> App<'a, 'v, 'ab, 'u, 'h, 'ar>{
31263185
None => (String::new(), None),
31273186
}
31283187
}
3188+
31293189
}

src/macros.rs

+56
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,62 @@ macro_rules! vec_remove {
5454
}
5555
}
5656

57+
macro_rules! remove_override {
58+
($me:ident, $name:expr) => ({
59+
if let Some(ref o) = $me.opts.get($name) {
60+
if let Some(ref ora) = o.requires {
61+
for a in ora {
62+
vec_remove!($me.required, a);
63+
}
64+
}
65+
if let Some(ref ora) = o.blacklist {
66+
for a in ora {
67+
vec_remove!($me.blacklist, a);
68+
}
69+
}
70+
if let Some(ref ora) = o.overrides {
71+
for a in ora {
72+
vec_remove!($me.overrides, a);
73+
}
74+
}
75+
} else if let Some(ref o) = $me.flags.get($name) {
76+
if let Some(ref ora) = o.requires {
77+
for a in ora {
78+
vec_remove!($me.required, a);
79+
}
80+
}
81+
if let Some(ref ora) = o.blacklist {
82+
for a in ora {
83+
vec_remove!($me.blacklist, a);
84+
}
85+
}
86+
if let Some(ref ora) = o.overrides {
87+
for a in ora {
88+
vec_remove!($me.overrides, a);
89+
}
90+
}
91+
} else if let Some(p) = $me.positionals_name.get($name) {
92+
if let Some(ref o) = $me.positionals_idx.get(p) {
93+
if let Some(ref ora) = o.requires {
94+
for a in ora {
95+
vec_remove!($me.required, a);
96+
}
97+
}
98+
if let Some(ref ora) = o.blacklist {
99+
for a in ora {
100+
vec_remove!($me.blacklist, a);
101+
}
102+
}
103+
if let Some(ref ora) = o.overrides {
104+
for a in ora {
105+
vec_remove!($me.overrides, a);
106+
}
107+
}
108+
}
109+
}
110+
})
111+
}
112+
57113
// De-duplication macro used in src/app.rs
58114
macro_rules! print_opt_help {
59115
($me:ident, $opt:ident, $spc:expr) => {

0 commit comments

Comments
 (0)