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