• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Internal
2 use crate::builder::StyledStr;
3 use crate::builder::{Arg, ArgGroup, ArgPredicate, Command, PossibleValue};
4 use crate::error::{Error, Result as ClapResult};
5 use crate::output::Usage;
6 use crate::parser::{ArgMatcher, ParseState};
7 use crate::util::ChildGraph;
8 use crate::util::FlatMap;
9 use crate::util::FlatSet;
10 use crate::util::Id;
11 use crate::INTERNAL_ERROR_MSG;
12 
13 pub(crate) struct Validator<'cmd> {
14     cmd: &'cmd Command,
15     required: ChildGraph<Id>,
16 }
17 
18 impl<'cmd> Validator<'cmd> {
new(cmd: &'cmd Command) -> Self19     pub(crate) fn new(cmd: &'cmd Command) -> Self {
20         let required = cmd.required_graph();
21         Validator { cmd, required }
22     }
23 
validate( &mut self, parse_state: ParseState, matcher: &mut ArgMatcher, ) -> ClapResult<()>24     pub(crate) fn validate(
25         &mut self,
26         parse_state: ParseState,
27         matcher: &mut ArgMatcher,
28     ) -> ClapResult<()> {
29         debug!("Validator::validate");
30         let conflicts = Conflicts::with_args(self.cmd, matcher);
31         let has_subcmd = matcher.subcommand_name().is_some();
32 
33         if let ParseState::Opt(a) = parse_state {
34             debug!("Validator::validate: needs_val_of={:?}", a);
35 
36             let o = &self.cmd[&a];
37             let should_err = if let Some(v) = matcher.args.get(o.get_id()) {
38                 v.all_val_groups_empty() && o.get_min_vals() != 0
39             } else {
40                 true
41             };
42             if should_err {
43                 return Err(Error::empty_value(
44                     self.cmd,
45                     &get_possible_values_cli(o)
46                         .iter()
47                         .filter(|pv| !pv.is_hide_set())
48                         .map(|n| n.get_name().to_owned())
49                         .collect::<Vec<_>>(),
50                     o.to_string(),
51                 ));
52             }
53         }
54 
55         if !has_subcmd && self.cmd.is_arg_required_else_help_set() {
56             let num_user_values = matcher
57                 .args()
58                 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
59                 .count();
60             if num_user_values == 0 {
61                 let message = self.cmd.write_help_err(false);
62                 return Err(Error::display_help_error(self.cmd, message));
63             }
64         }
65         if !has_subcmd && self.cmd.is_subcommand_required_set() {
66             let bn = self
67                 .cmd
68                 .get_bin_name()
69                 .unwrap_or_else(|| self.cmd.get_name());
70             return Err(Error::missing_subcommand(
71                 self.cmd,
72                 bn.to_string(),
73                 self.cmd
74                     .all_subcommand_names()
75                     .map(|s| s.to_owned())
76                     .collect::<Vec<_>>(),
77                 Usage::new(self.cmd)
78                     .required(&self.required)
79                     .create_usage_with_title(&[]),
80             ));
81         }
82 
83         ok!(self.validate_conflicts(matcher, &conflicts));
84         if !(self.cmd.is_subcommand_negates_reqs_set() && has_subcmd) {
85             ok!(self.validate_required(matcher, &conflicts));
86         }
87 
88         Ok(())
89     }
90 
validate_conflicts( &mut self, matcher: &ArgMatcher, conflicts: &Conflicts, ) -> ClapResult<()>91     fn validate_conflicts(
92         &mut self,
93         matcher: &ArgMatcher,
94         conflicts: &Conflicts,
95     ) -> ClapResult<()> {
96         debug!("Validator::validate_conflicts");
97 
98         ok!(self.validate_exclusive(matcher));
99 
100         for (arg_id, _) in matcher
101             .args()
102             .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
103             .filter(|(arg_id, _)| self.cmd.find(arg_id).is_some())
104         {
105             debug!("Validator::validate_conflicts::iter: id={:?}", arg_id);
106             let conflicts = conflicts.gather_conflicts(self.cmd, arg_id);
107             ok!(self.build_conflict_err(arg_id, &conflicts, matcher));
108         }
109 
110         Ok(())
111     }
112 
validate_exclusive(&self, matcher: &ArgMatcher) -> ClapResult<()>113     fn validate_exclusive(&self, matcher: &ArgMatcher) -> ClapResult<()> {
114         debug!("Validator::validate_exclusive");
115         let args_count = matcher
116             .args()
117             .filter(|(arg_id, matched)| {
118                 matched.check_explicit(&crate::builder::ArgPredicate::IsPresent)
119                     // Avoid including our own groups by checking none of them.  If a group is present, the
120                     // args for the group will be.
121                     && self.cmd.find(arg_id).is_some()
122             })
123             .count();
124         if args_count <= 1 {
125             // Nothing present to conflict with
126             return Ok(());
127         }
128 
129         matcher
130             .args()
131             .filter(|(_, matched)| matched.check_explicit(&crate::builder::ArgPredicate::IsPresent))
132             .filter_map(|(id, _)| {
133                 debug!("Validator::validate_exclusive:iter:{:?}", id);
134                 self.cmd
135                     .find(id)
136                     // Find `arg`s which are exclusive but also appear with other args.
137                     .filter(|&arg| arg.is_exclusive_set() && args_count > 1)
138             })
139             // Throw an error for the first conflict found.
140             .try_for_each(|arg| {
141                 Err(Error::argument_conflict(
142                     self.cmd,
143                     arg.to_string(),
144                     Vec::new(),
145                     Usage::new(self.cmd)
146                         .required(&self.required)
147                         .create_usage_with_title(&[]),
148                 ))
149             })
150     }
151 
build_conflict_err( &self, name: &Id, conflict_ids: &[Id], matcher: &ArgMatcher, ) -> ClapResult<()>152     fn build_conflict_err(
153         &self,
154         name: &Id,
155         conflict_ids: &[Id],
156         matcher: &ArgMatcher,
157     ) -> ClapResult<()> {
158         if conflict_ids.is_empty() {
159             return Ok(());
160         }
161 
162         debug!("Validator::build_conflict_err: name={:?}", name);
163         let mut seen = FlatSet::new();
164         let conflicts = conflict_ids
165             .iter()
166             .flat_map(|c_id| {
167                 if self.cmd.find_group(c_id).is_some() {
168                     self.cmd.unroll_args_in_group(c_id)
169                 } else {
170                     vec![c_id.clone()]
171                 }
172             })
173             .filter_map(|c_id| {
174                 seen.insert(c_id.clone()).then(|| {
175                     let c_arg = self.cmd.find(&c_id).expect(INTERNAL_ERROR_MSG);
176                     c_arg.to_string()
177                 })
178             })
179             .collect();
180 
181         let former_arg = self.cmd.find(name).expect(INTERNAL_ERROR_MSG);
182         let usg = self.build_conflict_err_usage(matcher, conflict_ids);
183         Err(Error::argument_conflict(
184             self.cmd,
185             former_arg.to_string(),
186             conflicts,
187             usg,
188         ))
189     }
190 
build_conflict_err_usage( &self, matcher: &ArgMatcher, conflicting_keys: &[Id], ) -> Option<StyledStr>191     fn build_conflict_err_usage(
192         &self,
193         matcher: &ArgMatcher,
194         conflicting_keys: &[Id],
195     ) -> Option<StyledStr> {
196         let used_filtered: Vec<Id> = matcher
197             .args()
198             .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
199             .map(|(n, _)| n)
200             .filter(|n| {
201                 // Filter out the args we don't want to specify.
202                 self.cmd.find(n).map_or(false, |a| !a.is_hide_set())
203             })
204             .filter(|key| !conflicting_keys.contains(key))
205             .cloned()
206             .collect();
207         let required: Vec<Id> = used_filtered
208             .iter()
209             .filter_map(|key| self.cmd.find(key))
210             .flat_map(|arg| arg.requires.iter().map(|item| &item.1))
211             .filter(|key| !used_filtered.contains(key) && !conflicting_keys.contains(key))
212             .chain(used_filtered.iter())
213             .cloned()
214             .collect();
215         Usage::new(self.cmd)
216             .required(&self.required)
217             .create_usage_with_title(&required)
218     }
219 
gather_requires(&mut self, matcher: &ArgMatcher)220     fn gather_requires(&mut self, matcher: &ArgMatcher) {
221         debug!("Validator::gather_requires");
222         for (name, matched) in matcher
223             .args()
224             .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
225         {
226             debug!("Validator::gather_requires:iter:{:?}", name);
227             if let Some(arg) = self.cmd.find(name) {
228                 let is_relevant = |(val, req_arg): &(ArgPredicate, Id)| -> Option<Id> {
229                     let required = matched.check_explicit(val);
230                     required.then(|| req_arg.clone())
231                 };
232 
233                 for req in self.cmd.unroll_arg_requires(is_relevant, arg.get_id()) {
234                     self.required.insert(req);
235                 }
236             } else if let Some(g) = self.cmd.find_group(name) {
237                 debug!("Validator::gather_requires:iter:{:?}:group", name);
238                 for r in &g.requires {
239                     self.required.insert(r.clone());
240                 }
241             }
242         }
243     }
244 
validate_required(&mut self, matcher: &ArgMatcher, conflicts: &Conflicts) -> ClapResult<()>245     fn validate_required(&mut self, matcher: &ArgMatcher, conflicts: &Conflicts) -> ClapResult<()> {
246         debug!("Validator::validate_required: required={:?}", self.required);
247         self.gather_requires(matcher);
248 
249         let mut missing_required = Vec::new();
250         let mut highest_index = 0;
251 
252         let is_exclusive_present = matcher
253             .args()
254             .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
255             .any(|(id, _)| {
256                 self.cmd
257                     .find(id)
258                     .map(|arg| arg.is_exclusive_set())
259                     .unwrap_or_default()
260             });
261         debug!(
262             "Validator::validate_required: is_exclusive_present={}",
263             is_exclusive_present
264         );
265 
266         for arg_or_group in self
267             .required
268             .iter()
269             .filter(|r| !matcher.check_explicit(r, &ArgPredicate::IsPresent))
270         {
271             debug!("Validator::validate_required:iter:aog={:?}", arg_or_group);
272             if let Some(arg) = self.cmd.find(arg_or_group) {
273                 debug!("Validator::validate_required:iter: This is an arg");
274                 if !is_exclusive_present && !self.is_missing_required_ok(arg, conflicts) {
275                     debug!(
276                         "Validator::validate_required:iter: Missing {:?}",
277                         arg.get_id()
278                     );
279                     missing_required.push(arg.get_id().clone());
280                     if !arg.is_last_set() {
281                         highest_index = highest_index.max(arg.get_index().unwrap_or(0));
282                     }
283                 }
284             } else if let Some(group) = self.cmd.find_group(arg_or_group) {
285                 debug!("Validator::validate_required:iter: This is a group");
286                 if !self
287                     .cmd
288                     .unroll_args_in_group(&group.id)
289                     .iter()
290                     .any(|a| matcher.check_explicit(a, &ArgPredicate::IsPresent))
291                 {
292                     debug!(
293                         "Validator::validate_required:iter: Missing {:?}",
294                         group.get_id()
295                     );
296                     missing_required.push(group.get_id().clone());
297                 }
298             }
299         }
300 
301         // Validate the conditionally required args
302         for a in self
303             .cmd
304             .get_arguments()
305             .filter(|a| !matcher.check_explicit(a.get_id(), &ArgPredicate::IsPresent))
306         {
307             let mut required = false;
308 
309             for (other, val) in &a.r_ifs {
310                 if matcher.check_explicit(other, &ArgPredicate::Equals(val.into())) {
311                     debug!(
312                         "Validator::validate_required:iter: Missing {:?}",
313                         a.get_id()
314                     );
315                     required = true;
316                 }
317             }
318 
319             let match_all = a.r_ifs_all.iter().all(|(other, val)| {
320                 matcher.check_explicit(other, &ArgPredicate::Equals(val.into()))
321             });
322             if match_all && !a.r_ifs_all.is_empty() {
323                 debug!(
324                     "Validator::validate_required:iter: Missing {:?}",
325                     a.get_id()
326                 );
327                 required = true;
328             }
329 
330             if (!a.r_unless.is_empty() || !a.r_unless_all.is_empty())
331                 && self.fails_arg_required_unless(a, matcher)
332             {
333                 debug!(
334                     "Validator::validate_required:iter: Missing {:?}",
335                     a.get_id()
336                 );
337                 required = true;
338             }
339 
340             if required {
341                 missing_required.push(a.get_id().clone());
342                 if !a.is_last_set() {
343                     highest_index = highest_index.max(a.get_index().unwrap_or(0));
344                 }
345             }
346         }
347 
348         // For display purposes, include all of the preceding positional arguments
349         if !self.cmd.is_allow_missing_positional_set() {
350             for pos in self
351                 .cmd
352                 .get_positionals()
353                 .filter(|a| !matcher.check_explicit(a.get_id(), &ArgPredicate::IsPresent))
354             {
355                 if pos.get_index() < Some(highest_index) {
356                     debug!(
357                         "Validator::validate_required:iter: Missing {:?}",
358                         pos.get_id()
359                     );
360                     missing_required.push(pos.get_id().clone());
361                 }
362             }
363         }
364 
365         if !missing_required.is_empty() {
366             ok!(self.missing_required_error(matcher, missing_required));
367         }
368 
369         Ok(())
370     }
371 
is_missing_required_ok(&self, a: &Arg, conflicts: &Conflicts) -> bool372     fn is_missing_required_ok(&self, a: &Arg, conflicts: &Conflicts) -> bool {
373         debug!("Validator::is_missing_required_ok: {}", a.get_id());
374         if !conflicts.gather_conflicts(self.cmd, a.get_id()).is_empty() {
375             debug!("Validator::is_missing_required_ok: true (self)");
376             return true;
377         }
378         for group_id in self.cmd.groups_for_arg(a.get_id()) {
379             if !conflicts.gather_conflicts(self.cmd, &group_id).is_empty() {
380                 debug!("Validator::is_missing_required_ok: true ({})", group_id);
381                 return true;
382             }
383         }
384         false
385     }
386 
387     // Failing a required unless means, the arg's "unless" wasn't present, and neither were they
fails_arg_required_unless(&self, a: &Arg, matcher: &ArgMatcher) -> bool388     fn fails_arg_required_unless(&self, a: &Arg, matcher: &ArgMatcher) -> bool {
389         debug!("Validator::fails_arg_required_unless: a={:?}", a.get_id());
390         let exists = |id| matcher.check_explicit(id, &ArgPredicate::IsPresent);
391 
392         (a.r_unless_all.is_empty() || !a.r_unless_all.iter().all(exists))
393             && !a.r_unless.iter().any(exists)
394     }
395 
396     // `req_args`: an arg to include in the error even if not used
missing_required_error( &self, matcher: &ArgMatcher, raw_req_args: Vec<Id>, ) -> ClapResult<()>397     fn missing_required_error(
398         &self,
399         matcher: &ArgMatcher,
400         raw_req_args: Vec<Id>,
401     ) -> ClapResult<()> {
402         debug!("Validator::missing_required_error; incl={:?}", raw_req_args);
403         debug!(
404             "Validator::missing_required_error: reqs={:?}",
405             self.required
406         );
407 
408         let usg = Usage::new(self.cmd).required(&self.required);
409 
410         let req_args = {
411             #[cfg(feature = "usage")]
412             {
413                 usg.get_required_usage_from(&raw_req_args, Some(matcher), true)
414                     .into_iter()
415                     .map(|s| s.to_string())
416                     .collect::<Vec<_>>()
417             }
418 
419             #[cfg(not(feature = "usage"))]
420             {
421                 raw_req_args
422                     .iter()
423                     .map(|id| {
424                         if let Some(arg) = self.cmd.find(id) {
425                             arg.to_string()
426                         } else if let Some(_group) = self.cmd.find_group(id) {
427                             self.cmd.format_group(id).to_string()
428                         } else {
429                             debug_assert!(false, "id={:?} is unknown", id);
430                             "".to_owned()
431                         }
432                     })
433                     .collect::<Vec<_>>()
434             }
435         };
436 
437         debug!(
438             "Validator::missing_required_error: req_args={:#?}",
439             req_args
440         );
441 
442         let used: Vec<Id> = matcher
443             .args()
444             .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
445             .map(|(n, _)| n)
446             .filter(|n| {
447                 // Filter out the args we don't want to specify.
448                 self.cmd.find(n).map_or(false, |a| !a.is_hide_set())
449             })
450             .cloned()
451             .chain(raw_req_args)
452             .collect();
453 
454         Err(Error::missing_required_argument(
455             self.cmd,
456             req_args,
457             usg.create_usage_with_title(&used),
458         ))
459     }
460 }
461 
462 #[derive(Default, Clone, Debug)]
463 struct Conflicts {
464     potential: FlatMap<Id, Vec<Id>>,
465 }
466 
467 impl Conflicts {
with_args(cmd: &Command, matcher: &ArgMatcher) -> Self468     fn with_args(cmd: &Command, matcher: &ArgMatcher) -> Self {
469         let mut potential = FlatMap::new();
470         potential.extend_unchecked(
471             matcher
472                 .args()
473                 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
474                 .map(|(id, _)| {
475                     let conf = gather_direct_conflicts(cmd, id);
476                     (id.clone(), conf)
477                 }),
478         );
479         Self { potential }
480     }
481 
gather_conflicts(&self, cmd: &Command, arg_id: &Id) -> Vec<Id>482     fn gather_conflicts(&self, cmd: &Command, arg_id: &Id) -> Vec<Id> {
483         debug!("Conflicts::gather_conflicts: arg={:?}", arg_id);
484         let mut conflicts = Vec::new();
485 
486         let arg_id_conflicts_storage;
487         let arg_id_conflicts = if let Some(arg_id_conflicts) = self.get_direct_conflicts(arg_id) {
488             arg_id_conflicts
489         } else {
490             // `is_missing_required_ok` is a case where we check not-present args for conflicts
491             arg_id_conflicts_storage = gather_direct_conflicts(cmd, arg_id);
492             &arg_id_conflicts_storage
493         };
494         for (other_arg_id, other_arg_id_conflicts) in self.potential.iter() {
495             if arg_id == other_arg_id {
496                 continue;
497             }
498 
499             if arg_id_conflicts.contains(other_arg_id) {
500                 conflicts.push(other_arg_id.clone());
501             }
502             if other_arg_id_conflicts.contains(arg_id) {
503                 conflicts.push(other_arg_id.clone());
504             }
505         }
506 
507         debug!("Conflicts::gather_conflicts: conflicts={:?}", conflicts);
508         conflicts
509     }
510 
get_direct_conflicts(&self, arg_id: &Id) -> Option<&[Id]>511     fn get_direct_conflicts(&self, arg_id: &Id) -> Option<&[Id]> {
512         self.potential.get(arg_id).map(Vec::as_slice)
513     }
514 }
515 
gather_direct_conflicts(cmd: &Command, id: &Id) -> Vec<Id>516 fn gather_direct_conflicts(cmd: &Command, id: &Id) -> Vec<Id> {
517     let conf = if let Some(arg) = cmd.find(id) {
518         gather_arg_direct_conflicts(cmd, arg)
519     } else if let Some(group) = cmd.find_group(id) {
520         gather_group_direct_conflicts(group)
521     } else {
522         debug_assert!(false, "id={:?} is unknown", id);
523         Vec::new()
524     };
525     debug!(
526         "Conflicts::gather_direct_conflicts id={:?}, conflicts={:?}",
527         id, conf
528     );
529     conf
530 }
531 
gather_arg_direct_conflicts(cmd: &Command, arg: &Arg) -> Vec<Id>532 fn gather_arg_direct_conflicts(cmd: &Command, arg: &Arg) -> Vec<Id> {
533     let mut conf = arg.blacklist.clone();
534     for group_id in cmd.groups_for_arg(arg.get_id()) {
535         let group = cmd.find_group(&group_id).expect(INTERNAL_ERROR_MSG);
536         conf.extend(group.conflicts.iter().cloned());
537         if !group.multiple {
538             for member_id in &group.args {
539                 if member_id != arg.get_id() {
540                     conf.push(member_id.clone());
541                 }
542             }
543         }
544     }
545 
546     // Overrides are implicitly conflicts
547     conf.extend(arg.overrides.iter().cloned());
548 
549     conf
550 }
551 
gather_group_direct_conflicts(group: &ArgGroup) -> Vec<Id>552 fn gather_group_direct_conflicts(group: &ArgGroup) -> Vec<Id> {
553     group.conflicts.clone()
554 }
555 
get_possible_values_cli(a: &Arg) -> Vec<PossibleValue>556 pub(crate) fn get_possible_values_cli(a: &Arg) -> Vec<PossibleValue> {
557     if !a.is_takes_value_set() {
558         vec![]
559     } else {
560         a.get_value_parser()
561             .possible_values()
562             .map(|pvs| pvs.collect())
563             .unwrap_or_default()
564     }
565 }
566