• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

clap-rs / clap / 900578139

pending completion
900578139

Pull #2512

github

GitHub
Merge 5fe3f4780 into a8134ddcd
Pull Request #2512: refactor(validator): cleanup code

105 of 105 new or added lines in 1 file covered. (100.0%)

11181 of 12996 relevant lines covered (86.03%)

11.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.78
/src/parse/validator.rs
1
// Internal
2
use crate::{
3
    build::{AppSettings as AS, Arg, ArgSettings},
4
    output::Usage,
5
    parse::{
6
        errors::{Error, ErrorKind, Result as ClapResult},
7
        ArgMatcher, MatchedArg, ParseResult, Parser, ValueType,
8
    },
9
    util::{ChildGraph, Id},
10
    INTERNAL_ERROR_MSG, INVALID_UTF8,
11
};
12

13
pub(crate) struct Validator<'help, 'app, 'parser> {
14
    p: &'parser mut Parser<'help, 'app>,
15
    c: ChildGraph<Id>,
16
}
17

18
impl<'help, 'app, 'parser> Validator<'help, 'app, 'parser> {
19
    pub(crate) fn new(p: &'parser mut Parser<'help, 'app>) -> Self {
77✔
20
        Validator {
21
            p,
22
            c: ChildGraph::with_capacity(5),
77✔
23
        }
24
    }
25

26
    pub(crate) fn validate(
77✔
27
        &mut self,
28
        needs_val_of: ParseResult,
29
        is_subcmd: bool,
30
        matcher: &mut ArgMatcher,
31
    ) -> ClapResult<()> {
32
        debug!("Validator::validate");
×
33
        let mut reqs_validated = false;
77✔
34
        self.p.add_env(matcher)?;
77✔
35
        self.p.add_defaults(matcher);
76✔
36
        if let ParseResult::Opt(a) = needs_val_of {
74✔
37
            debug!("Validator::validate: needs_val_of={:?}", a);
×
38
            self.validate_required(matcher)?;
21✔
39

40
            let o = &self.p.app[&a];
42✔
41
            reqs_validated = true;
21✔
42
            let should_err = if let Some(v) = matcher.0.args.get(&o.id) {
21✔
43
                v.is_vals_empty() && !(o.min_vals.is_some() && o.min_vals.unwrap() == 0)
21✔
44
            } else {
45
                true
×
46
            };
47
            if should_err {
21✔
48
                return Err(Error::empty_value(
6✔
49
                    o,
×
50
                    Usage::new(self.p).create_usage_with_title(&[]),
6✔
51
                    self.p.app.color(),
3✔
52
                ));
53
            }
54
        }
55

56
        if matcher.is_empty()
263✔
57
            && matcher.subcommand_name().is_none()
78✔
58
            && self.p.is_set(AS::ArgRequiredElseHelp)
32✔
59
        {
60
            let message = self.p.write_help_err()?;
7✔
61
            return Err(Error {
4✔
62
                message,
4✔
63
                kind: ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand,
×
64
                info: vec![],
4✔
65
                source: None,
4✔
66
            });
67
        }
68
        self.validate_conflicts(matcher)?;
149✔
69
        if !(self.p.is_set(AS::SubcommandsNegateReqs) && is_subcmd || reqs_validated) {
186✔
70
            self.validate_required(matcher)?;
144✔
71
            self.validate_required_unless(matcher)?;
145✔
72
        }
73
        self.validate_matched_args(matcher)?;
76✔
74

75
        Ok(())
78✔
76
    }
77

78
    fn validate_arg_values(
79✔
79
        &self,
80
        arg: &Arg,
81
        ma: &MatchedArg,
82
        matcher: &ArgMatcher,
83
    ) -> ClapResult<()> {
84
        debug!("Validator::validate_arg_values: arg={:?}", arg.name);
×
85
        for val in ma.vals_flatten() {
147✔
86
            if self.p.is_set(AS::StrictUtf8) && val.to_str().is_none() {
65✔
87
                debug!(
×
88
                    "Validator::validate_arg_values: invalid UTF-8 found in val {:?}",
89
                    val
×
90
                );
91
                return Err(Error::invalid_utf8(
4✔
92
                    Usage::new(self.p).create_usage_with_title(&[]),
2✔
93
                    self.p.app.color(),
2✔
94
                ));
95
            }
96
            if !arg.possible_vals.is_empty() {
198✔
97
                debug!(
×
98
                    "Validator::validate_arg_values: possible_vals={:?}",
99
                    arg.possible_vals
×
100
                );
101
                let val_str = val.to_string_lossy();
10✔
102
                let ok = if arg.is_set(ArgSettings::IgnoreCase) {
5✔
103
                    arg.possible_vals
8✔
104
                        .iter()
105
                        .any(|pv| pv.eq_ignore_ascii_case(&val_str))
6✔
106
                } else {
107
                    arg.possible_vals.contains(&&*val_str)
15✔
108
                };
109
                if !ok {
5✔
110
                    let used: Vec<Id> = matcher
6✔
111
                        .arg_names()
112
                        .filter(|&n| {
9✔
113
                            self.p.app.find(n).map_or(true, |a| {
6✔
114
                                !(a.is_set(ArgSettings::Hidden) || self.p.required.contains(&a.id))
6✔
115
                            })
116
                        })
117
                        .cloned()
118
                        .collect();
119
                    return Err(Error::invalid_value(
9✔
120
                        val_str.to_string(),
3✔
121
                        &arg.possible_vals,
3✔
122
                        arg,
×
123
                        Usage::new(self.p).create_usage_with_title(&used),
3✔
124
                        self.p.app.color(),
4✔
125
                    ));
126
                }
127
            }
128
            if arg.is_set(ArgSettings::ForbidEmptyValues)
202✔
129
                && val.is_empty()
2✔
130
                && matcher.contains(&arg.id)
1✔
131
            {
132
                debug!("Validator::validate_arg_values: illegal empty val found");
×
133
                return Err(Error::empty_value(
2✔
134
                    arg,
×
135
                    Usage::new(self.p).create_usage_with_title(&[]),
2✔
136
                    self.p.app.color(),
1✔
137
                ));
138
            }
139

140
            if let Some(ref vtor) = arg.validator {
112✔
141
                debug!("Validator::validate_arg_values: checking validator...");
×
142
                let mut vtor = vtor.lock().unwrap();
30✔
143
                if let Err(e) = vtor(&*val.to_string_lossy()) {
36✔
144
                    debug!("error");
×
145
                    return Err(Error::value_validation(
12✔
146
                        arg.to_string(),
6✔
147
                        val.to_string_lossy().to_string(),
6✔
148
                        e,
6✔
149
                        self.p.app.color(),
6✔
150
                    ));
151
                } else {
152
                    debug!("good");
×
153
                }
154
            }
155
            if let Some(ref vtor) = arg.validator_os {
67✔
156
                debug!("Validator::validate_arg_values: checking validator_os...");
×
157
                let mut vtor = vtor.lock().unwrap();
1✔
158
                if let Err(e) = vtor(val) {
1✔
159
                    debug!("error");
×
160
                    return Err(Error::value_validation(
×
161
                        arg.to_string(),
×
162
                        val.to_string_lossy().into(),
×
163
                        e,
×
164
                        self.p.app.color(),
×
165
                    ));
166
                } else {
167
                    debug!("good");
×
168
                }
169
            }
170
        }
171
        Ok(())
78✔
172
    }
173

174
    fn build_conflict_err_usage(
3✔
175
        &self,
176
        matcher: &ArgMatcher,
177
        retained_arg: &Arg,
178
        conflicting_key: &Id,
179
    ) -> String {
180
        let retained_blacklist = &retained_arg.blacklist;
3✔
181
        let used_filtered: Vec<Id> = matcher
6✔
182
            .arg_names()
183
            .filter(|key| *key != conflicting_key && !retained_blacklist.contains(key))
9✔
184
            .cloned()
185
            .collect();
186
        let required: Vec<Id> = used_filtered
12✔
187
            .iter()
188
            .filter_map(|key| self.p.app.find(key))
9✔
189
            .flat_map(|arg| arg.requires.iter().map(|item| &item.1))
10✔
190
            .filter(|key| {
5✔
191
                !used_filtered.contains(key)
6✔
192
                    && *key != conflicting_key
1✔
193
                    && !retained_blacklist.contains(key)
2✔
194
            })
195
            .chain(used_filtered.iter())
3✔
196
            .cloned()
197
            .collect();
198
        Usage::new(self.p).create_usage_with_title(&required)
3✔
199
    }
200

201
    fn build_conflict_err(&self, name: &Id, matcher: &ArgMatcher) -> ClapResult<()> {
5✔
202
        debug!("Validator::build_conflict_err: name={:?}", name);
×
203
        if let Some(checked_arg) = self.p.app.find(name) {
18✔
204
            matcher
9✔
205
                .arg_names()
206
                .filter_map(|k| {
6✔
207
                    let arg = self.p.app.find(k);
3✔
208
                    // For an arg that blacklists `name`, this will return `Some((k, a))` to indicate a conflict.
209
                    arg.filter(|a| a.blacklist.contains(&name)).map(|a| (k, a))
15✔
210
                })
211
                .try_for_each(|(k, a)| {
9✔
212
                    // The error will be then constructed according to the first conflict.
213
                    let (_former, former_arg, latter, latter_arg) = {
2✔
214
                        let name_pos = matcher.arg_names().position(|key| key == name);
9✔
215
                        let k_pos = matcher.arg_names().position(|key| key == k);
9✔
216
                        if name_pos < k_pos {
5✔
217
                            (name, checked_arg, k, a)
2✔
218
                        } else {
219
                            (k, a, name, checked_arg)
1✔
220
                        }
221
                    };
222
                    let usg = self.build_conflict_err_usage(matcher, former_arg, latter);
3✔
223
                    Err(Error::argument_conflict(
6✔
224
                        latter_arg,
×
225
                        Some(former_arg.to_string()),
3✔
226
                        usg,
3✔
227
                        self.p.app.color(),
3✔
228
                    ))
229
                })
230
        } else if let Some(g) = self.p.app.groups.iter().find(|x| x.id == *name) {
16✔
231
            let usg = Usage::new(self.p).create_usage_with_title(&[]);
4✔
232
            let args_in_group = self.p.app.unroll_args_in_group(&g.id);
4✔
233
            let first = matcher
8✔
234
                .arg_names()
235
                .find(|x| args_in_group.contains(x))
12✔
236
                .expect(INTERNAL_ERROR_MSG);
×
237
            let c_with = matcher
12✔
238
                .arg_names()
239
                .find(|x| x != &first && args_in_group.contains(x))
12✔
240
                .map(|x| self.p.app[x].to_string());
12✔
241
            debug!("Validator::build_conflict_err: c_with={:?}:group", c_with);
×
242
            Err(Error::argument_conflict(
8✔
243
                &self.p.app[first],
4✔
244
                c_with,
4✔
245
                usg,
4✔
246
                self.p.app.color(),
4✔
247
            ))
248
        } else {
249
            Ok(())
×
250
        }?;
251

252
        panic!("{}", INTERNAL_ERROR_MSG);
×
253
    }
254

255
    fn validate_conflicts(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> {
74✔
256
        debug!("Validator::validate_conflicts");
×
257
        self.validate_exclusive(matcher)?;
74✔
258
        self.gather_conflicts(matcher);
75✔
259

260
        self.c
224✔
261
            .iter()
262
            .filter(|&name| {
86✔
263
                debug!("Validator::validate_conflicts:iter:{:?}", name);
×
264
                // Filter out the conflicting cases.
265
                if let Some(g) = self
16✔
266
                    .p
×
267
                    .app
×
268
                    .groups
×
269
                    .iter()
×
270
                    .find(|g| !g.multiple && g.id == *name)
14✔
271
                {
272
                    let conf_with_self = || {
8✔
273
                        self.p
13✔
274
                            .app
×
275
                            .unroll_args_in_group(&g.id)
4✔
276
                            .iter()
×
277
                            .filter(|&a| matcher.contains(a))
15✔
278
                            .count()
×
279
                            > 1
×
280
                    };
281
                    let conf_with_arg = || g.conflicts.iter().any(|x| matcher.contains(x));
16✔
282
                    let arg_conf_with_gr = || {
9✔
283
                        matcher
15✔
284
                            .arg_names()
×
285
                            .filter_map(|x| self.p.app.find(x))
15✔
286
                            .any(|x| x.blacklist.iter().any(|c| *c == g.id))
19✔
287
                    };
288
                    conf_with_self() || conf_with_arg() || arg_conf_with_gr()
4✔
289
                } else if let Some(ma) = matcher.get(name) {
19✔
290
                    debug!(
×
291
                        "Validator::validate_conflicts:iter:{:?}: matcher contains it...",
×
292
                        name
×
293
                    );
294
                    ma.occurs > 0
2✔
295
                } else {
296
                    false
5✔
297
                }
298
            })
299
            // Throw an error for the first conflict found.
300
            .try_for_each(|odd| self.build_conflict_err(odd, matcher))
84✔
301
    }
302

303
    fn validate_exclusive(&mut self, matcher: &mut ArgMatcher) -> ClapResult<()> {
75✔
304
        debug!("Validator::validate_exclusive");
×
305
        let args_count = matcher.arg_names().count();
75✔
306
        matcher
225✔
307
            .arg_names()
308
            .filter_map(|name| {
148✔
309
                debug!("Validator::validate_exclusive:iter:{:?}", name);
×
310
                self.p
146✔
311
                    .app
×
312
                    .find(name)
×
313
                    // Find `arg`s which are exclusive but also appear with other args.
314
                    .filter(|&arg| arg.exclusive && args_count > 1)
219✔
315
            })
316
            // Throw an error for the first conflict found.
317
            .try_for_each(|arg| {
76✔
318
                Err(Error::argument_conflict(
3✔
319
                    arg,
×
320
                    None,
1✔
321
                    Usage::new(self.p).create_usage_with_title(&[]),
1✔
322
                    self.p.app.color(),
1✔
323
                ))
324
            })
325
    }
326

327
    // Gathers potential conflicts based on used argument, but without considering requirements
328
    // and such
329
    fn gather_conflicts(&mut self, matcher: &mut ArgMatcher) {
75✔
330
        debug!("Validator::gather_conflicts");
×
331
        matcher
225✔
332
            .arg_names()
333
            .filter(|name| {
148✔
334
                debug!("Validator::gather_conflicts:iter: id={:?}", name);
×
335
                // if arg is "present" only because it got default value
336
                // it doesn't conflict with anything and should be skipped
337
                let skip = matcher
146✔
338
                    .get(name)
73✔
339
                    .map_or(false, |a| a.ty == ValueType::DefaultValue);
146✔
340
                if skip {
73✔
341
                    debug!("Validator::gather_conflicts:iter: This is default value, skipping.",);
×
342
                }
343
                !skip
73✔
344
            })
345
            .for_each(|name| {
148✔
346
                if let Some(arg) = self.p.app.find(name) {
146✔
347
                    // Since an arg was used, every arg it conflicts with is added to the conflicts
348
                    for conf in &arg.blacklist {
81✔
349
                        /*
350
                        for g_arg in self.p.app.unroll_args_in_group(&g.name) {
351
                            if &g_arg != name {
352
                                self.c.insert(g.id.clone()); // TODO ERROR is here - groups allow one arg but this line disallows all group args
353
                            }
354
                        }
355
                        */
356
                        self.c.insert(conf.clone());
8✔
357
                    }
358
                    // Now we need to know which groups this arg was a member of, to add all other
359
                    // args in that group to the conflicts, as well as any args those args conflict
360
                    // with
361
                    for grp in self.p.app.groups_for_arg(&name) {
151✔
362
                        if let Some(g) = self
16✔
363
                            .p
×
364
                            .app
×
365
                            .groups
×
366
                            .iter()
×
367
                            .find(|g| !g.multiple && g.id == grp)
18✔
368
                        {
369
                            /*
370
                            for g_arg in self.p.app.unroll_args_in_group(&g.name) {
371
                                if &g_arg != name {
372
                                    self.c.insert(g.id.clone());
373
                                }
374
                            }
375
                            */
376
                            self.c.insert(g.id.clone());
4✔
377
                        }
378
                    }
379
                } else if let Some(g) = self
21✔
380
                    .p
×
381
                    .app
×
382
                    .groups
×
383
                    .iter()
×
384
                    .find(|g| !g.multiple && g.id == *name)
18✔
385
                {
386
                    debug!("Validator::gather_conflicts:iter:{:?}:group", name);
×
387
                    self.c.insert(g.id.clone());
4✔
388
                }
389
            });
390
    }
391

392
    fn gather_requirements(&mut self, matcher: &ArgMatcher) {
74✔
393
        debug!("Validator::gather_requirements");
×
394
        for name in matcher.arg_names() {
149✔
395
            debug!("Validator::gather_requirements:iter:{:?}", name);
×
396
            if let Some(arg) = self.p.app.find(name) {
142✔
397
                for req in self.p.app.unroll_requirements_for_arg(&arg.id, matcher) {
77✔
398
                    self.p.required.insert(req);
5✔
399
                }
400
            } else if let Some(g) = self.p.app.groups.iter().find(|grp| grp.id == *name) {
20✔
401
                debug!("Validator::gather_conflicts:iter:{:?}:group", name);
×
402
                for r in &g.requires {
4✔
403
                    self.p.required.insert(r.clone());
×
404
                }
405
            }
406
        }
407
    }
408

409
    fn validate_matched_args(&self, matcher: &mut ArgMatcher) -> ClapResult<()> {
76✔
410
        debug!("Validator::validate_matched_args");
×
411
        matcher.iter().try_for_each(|(name, ma)| {
224✔
412
            debug!(
×
413
                "Validator::validate_matched_args:iter:{:?}: vals={:#?}",
×
414
                name,
×
415
                ma.vals_flatten()
×
416
            );
417
            if let Some(arg) = self.p.app.find(name) {
234✔
418
                self.validate_arg_num_vals(arg, ma)?;
75✔
419
                self.validate_arg_values(arg, ma, matcher)?;
156✔
420
                self.validate_arg_requires(arg, ma, matcher)?;
159✔
421
                self.validate_arg_num_occurs(arg, ma)?;
160✔
422
            } else {
423
                let grp = self
15✔
424
                    .p
×
425
                    .app
×
426
                    .groups
×
427
                    .iter()
×
428
                    .find(|g| g.id == *name)
15✔
429
                    .expect(INTERNAL_ERROR_MSG);
×
430
                if grp.requires.iter().any(|n| !matcher.contains(n)) {
5✔
431
                    return self.missing_required_error(matcher, vec![name.clone()]);
×
432
                }
433
            }
434
            Ok(())
81✔
435
        })
436
    }
437

438
    fn validate_arg_num_occurs(&self, a: &Arg, ma: &MatchedArg) -> ClapResult<()> {
80✔
439
        debug!(
×
440
            "Validator::validate_arg_num_occurs: {:?}={}",
441
            a.name, ma.occurs
×
442
        );
443
        // Occurrence of positional argument equals to number of values rather
444
        // than number of grouped values.
445
        if ma.occurs > 1 && !a.is_set(ArgSettings::MultipleOccurrences) && !a.is_positional() {
80✔
446
            // Not the first time, and we don't allow multiples
447
            return Err(Error::unexpected_multiple_usage(
6✔
448
                a,
×
449
                Usage::new(self.p).create_usage_with_title(&[]),
4✔
450
                self.p.app.color(),
3✔
451
            ));
452
        }
453
        Ok(())
80✔
454
    }
455

456
    fn validate_arg_num_vals(&self, a: &Arg, ma: &MatchedArg) -> ClapResult<()> {
75✔
457
        debug!("Validator::validate_arg_num_vals");
×
458
        if let Some(num) = a.num_vals {
82✔
459
            let total_num = ma.num_vals();
5✔
460
            debug!("Validator::validate_arg_num_vals: num_vals set...{}", num);
×
461
            let should_err = if a.is_set(ArgSettings::MultipleOccurrences) {
7✔
462
                total_num % num != 0
10✔
463
            } else {
464
                num != total_num
2✔
465
            };
466
            if should_err {
5✔
467
                debug!("Validator::validate_arg_num_vals: Sending error WrongNumberOfValues");
×
468
                return Err(Error::wrong_number_of_values(
2✔
469
                    a,
×
470
                    num,
×
471
                    if a.is_set(ArgSettings::MultipleOccurrences) {
3✔
472
                        total_num % num
2✔
473
                    } else {
474
                        total_num
1✔
475
                    },
476
                    Usage::new(self.p).create_usage_with_title(&[]),
1✔
477
                    self.p.app.color(),
1✔
478
                ));
479
            }
480
        }
481
        if let Some(num) = a.max_vals {
79✔
482
            debug!("Validator::validate_arg_num_vals: max_vals set...{}", num);
×
483
            if ma.num_vals() > num {
2✔
484
                debug!("Validator::validate_arg_num_vals: Sending error TooManyValues");
×
485
                return Err(Error::too_many_values(
4✔
486
                    ma.vals_flatten()
4✔
487
                        .last()
×
488
                        .expect(INTERNAL_ERROR_MSG)
×
489
                        .to_str()
×
490
                        .expect(INVALID_UTF8)
×
491
                        .to_string(),
×
492
                    a,
×
493
                    Usage::new(self.p).create_usage_with_title(&[]),
2✔
494
                    self.p.app.color(),
2✔
495
                ));
496
            }
497
        }
498
        let min_vals_zero = if let Some(num) = a.min_vals {
161✔
499
            debug!("Validator::validate_arg_num_vals: min_vals set: {}", num);
×
500
            if ma.num_vals() < num && num != 0 {
6✔
501
                debug!("Validator::validate_arg_num_vals: Sending error TooFewValues");
×
502
                return Err(Error::too_few_values(
2✔
503
                    a,
×
504
                    num,
×
505
                    ma.num_vals(),
1✔
506
                    Usage::new(self.p).create_usage_with_title(&[]),
1✔
507
                    self.p.app.color(),
1✔
508
                ));
509
            }
510
            num == 0
5✔
511
        } else {
512
            false
78✔
513
        };
514
        // Issue 665 (https://github.com/kbknapp/clap-rs/issues/665)
515
        // Issue 1105 (https://github.com/kbknapp/clap-rs/issues/1105)
516
        if a.is_set(ArgSettings::TakesValue) && !min_vals_zero && ma.is_vals_empty() {
79✔
517
            return Err(Error::empty_value(
3✔
518
                a,
×
519
                Usage::new(self.p).create_usage_with_title(&[]),
2✔
520
                self.p.app.color(),
1✔
521
            ));
522
        }
523
        Ok(())
77✔
524
    }
525

526
    fn validate_arg_requires(
80✔
527
        &self,
528
        a: &Arg<'help>,
529
        ma: &MatchedArg,
530
        matcher: &ArgMatcher,
531
    ) -> ClapResult<()> {
532
        debug!("Validator::validate_arg_requires:{:?}", a.name);
×
533
        for (val, name) in &a.requires {
84✔
534
            if let Some(val) = val {
11✔
535
                let missing_req = |v| v == val && !matcher.contains(&name);
6✔
536
                if ma.vals_flatten().any(missing_req) {
2✔
537
                    return self.missing_required_error(matcher, vec![a.id.clone()]);
×
538
                }
539
            } else if !matcher.contains(&name) {
6✔
540
                return self.missing_required_error(matcher, vec![name.clone()]);
×
541
            }
542
        }
543
        Ok(())
80✔
544
    }
545

546
    fn validate_required(&mut self, matcher: &ArgMatcher) -> ClapResult<()> {
74✔
547
        debug!(
×
548
            "Validator::validate_required: required={:?}",
549
            self.p.required
×
550
        );
551
        self.gather_requirements(matcher);
74✔
552

553
        for arg_or_group in self.p.required.iter().filter(|r| !matcher.contains(r)) {
143✔
554
            debug!("Validator::validate_required:iter:aog={:?}", arg_or_group);
×
555
            if let Some(arg) = self.p.app.find(&arg_or_group) {
22✔
556
                debug!("Validator::validate_required:iter: This is an arg");
×
557
                if !self.is_missing_required_ok(arg, matcher) {
9✔
558
                    return self.missing_required_error(matcher, vec![]);
18✔
559
                }
560
            } else if let Some(group) = self.p.app.groups.iter().find(|g| g.id == *arg_or_group) {
16✔
561
                debug!("Validator::validate_required:iter: This is a group");
×
562
                if !self
12✔
563
                    .p
×
564
                    .app
×
565
                    .unroll_args_in_group(&group.id)
×
566
                    .iter()
×
567
                    .any(|a| matcher.contains(a))
16✔
568
                {
569
                    return self.missing_required_error(matcher, vec![]);
8✔
570
                }
571
            }
572
        }
573

574
        // Validate the conditionally required args
575
        for a in self.p.app.args.args() {
225✔
576
            for (other, val) in &a.r_ifs {
79✔
577
                if let Some(ma) = matcher.get(other) {
7✔
578
                    if ma.contains_val(val) && !matcher.contains(&a.id) {
3✔
579
                        return self.missing_required_error(matcher, vec![a.id.clone()]);
4✔
580
                    }
581
                }
582
            }
583

584
            let match_all = a
225✔
585
                .r_ifs_all
×
586
                .iter()
587
                .all(|(other, val)| matcher.get(other).map_or(false, |ma| ma.contains_val(val)));
79✔
588
            if match_all && !a.r_ifs_all.is_empty() && !matcher.contains(&a.id) {
75✔
589
                return self.missing_required_error(matcher, vec![a.id.clone()]);
2✔
590
            }
591
        }
592
        Ok(())
74✔
593
    }
594

595
    fn is_missing_required_ok(&self, a: &Arg<'help>, matcher: &ArgMatcher) -> bool {
9✔
596
        debug!("Validator::is_missing_required_ok: {}", a.name);
×
597
        self.validate_arg_conflicts(a, matcher) || self.p.overridden.contains(&a.id)
18✔
598
    }
599

600
    fn validate_arg_conflicts(&self, a: &Arg<'help>, matcher: &ArgMatcher) -> bool {
9✔
601
        debug!("Validator::validate_arg_conflicts: a={:?}", a.name);
×
602
        a.blacklist.iter().any(|conf| {
10✔
603
            matcher.contains(conf)
3✔
604
                || self
4✔
605
                    .p
×
606
                    .app
×
607
                    .groups
×
608
                    .iter()
×
609
                    .find(|g| g.id == *conf)
1✔
610
                    .map_or(false, |g| g.args.iter().any(|arg| matcher.contains(arg)))
1✔
611
        })
612
    }
613

614
    fn validate_required_unless(&self, matcher: &ArgMatcher) -> ClapResult<()> {
71✔
615
        debug!("Validator::validate_required_unless");
×
616
        let failed_args: Vec<_> = self
143✔
617
            .p
×
618
            .app
×
619
            .args
×
620
            .args()
621
            .filter(|&a| {
216✔
622
                !a.r_unless.is_empty()
148✔
623
                    && !matcher.contains(&a.id)
2✔
624
                    && self.fails_arg_required_unless(a, matcher)
1✔
625
            })
626
            .map(|a| a.id.clone())
2✔
627
            .collect();
628
        if failed_args.is_empty() {
148✔
629
            Ok(())
74✔
630
        } else {
631
            self.missing_required_error(matcher, failed_args)
2✔
632
        }
633
    }
634

635
    // Failing a required unless means, the arg's "unless" wasn't present, and neither were they
636
    fn fails_arg_required_unless(&self, a: &Arg<'help>, matcher: &ArgMatcher) -> bool {
1✔
637
        debug!("Validator::fails_arg_required_unless: a={:?}", a.name);
×
638
        if a.is_set(ArgSettings::RequiredUnlessAll) {
2✔
639
            debug!("Validator::fails_arg_required_unless:{}:All", a.name);
×
640
            !a.r_unless.iter().all(|id| matcher.contains(id))
4✔
641
        } else {
642
            debug!("Validator::fails_arg_required_unless:{}:Any", a.name);
×
643
            !a.r_unless.iter().any(|id| matcher.contains(id))
4✔
644
        }
645
    }
646

647
    // `incl`: an arg to include in the error even if not used
648
    fn missing_required_error(&self, matcher: &ArgMatcher, incl: Vec<Id>) -> ClapResult<()> {
13✔
649
        debug!("Validator::missing_required_error; incl={:?}", incl);
×
650
        debug!(
×
651
            "Validator::missing_required_error: reqs={:?}",
652
            self.p.required
×
653
        );
654

655
        let usg = Usage::new(self.p);
13✔
656

657
        let req_args = usg.get_required_usage_from(&incl, Some(matcher), true);
13✔
658

659
        debug!(
×
660
            "Validator::missing_required_error: req_args={:#?}",
661
            req_args
×
662
        );
663

664
        let used: Vec<Id> = matcher
39✔
665
            .arg_names()
666
            .filter(|n| {
20✔
667
                self.p.app.find(n).map_or(true, |a| {
14✔
668
                    !(a.is_set(ArgSettings::Hidden) || self.p.required.contains(&a.id))
14✔
669
                })
670
            })
671
            .cloned()
672
            .chain(incl)
13✔
673
            .collect();
674

675
        Err(Error::missing_required_argument(
27✔
676
            req_args,
13✔
677
            usg.create_usage_with_title(&used),
13✔
678
            self.p.app.color(),
13✔
679
        ))
680
    }
681
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2024 Coveralls, Inc