1// Internal
2use crate::builder::StyledStr;
3use crate::builder::{Arg, ArgGroup, ArgPredicate, Command, PossibleValue};
4use crate::error::{Error, Result as ClapResult};
5use crate::output::Usage;
6use crate::parser::{ArgMatcher, ParseState};
7use crate::util::ChildGraph;
8use crate::util::FlatMap;
9use crate::util::FlatSet;
10use crate::util::Id;
11use crate::INTERNAL_ERROR_MSG;
12
13pub(crate) struct Validator<'cmd> {
14 cmd: &'cmd Command,
15 required: ChildGraph<Id>,
16}
17
18impl<'cmd> Validator<'cmd> {
19 pub(crate) fn new(cmd: &'cmd Command) -> Self {
20 let required = cmd.required_graph();
21 Validator { cmd, required }
22 }
23
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
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
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 .next()
140 .map(|arg| {
141 // Throw an error for the first conflict found.
142 Err(Error::argument_conflict(
143 self.cmd,
144 arg.to_string(),
145 Vec::new(),
146 Usage::new(self.cmd)
147 .required(&self.required)
148 .create_usage_with_title(&[]),
149 ))
150 })
151 .unwrap_or(Ok(()))
152 }
153
154 fn build_conflict_err(
155 &self,
156 name: &Id,
157 conflict_ids: &[Id],
158 matcher: &ArgMatcher,
159 ) -> ClapResult<()> {
160 if conflict_ids.is_empty() {
161 return Ok(());
162 }
163
164 debug!("Validator::build_conflict_err: name={name:?}");
165 let mut seen = FlatSet::new();
166 let conflicts = conflict_ids
167 .iter()
168 .flat_map(|c_id| {
169 if self.cmd.find_group(c_id).is_some() {
170 self.cmd.unroll_args_in_group(c_id)
171 } else {
172 vec![c_id.clone()]
173 }
174 })
175 .filter_map(|c_id| {
176 seen.insert(c_id.clone()).then(|| {
177 let c_arg = self.cmd.find(&c_id).expect(INTERNAL_ERROR_MSG);
178 c_arg.to_string()
179 })
180 })
181 .collect();
182
183 let former_arg = self.cmd.find(name).expect(INTERNAL_ERROR_MSG);
184 let usg = self.build_conflict_err_usage(matcher, conflict_ids);
185 Err(Error::argument_conflict(
186 self.cmd,
187 former_arg.to_string(),
188 conflicts,
189 usg,
190 ))
191 }
192
193 fn build_conflict_err_usage(
194 &self,
195 matcher: &ArgMatcher,
196 conflicting_keys: &[Id],
197 ) -> Option<StyledStr> {
198 let used_filtered: Vec<Id> = matcher
199 .args()
200 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
201 .map(|(n, _)| n)
202 .filter(|n| {
203 // Filter out the args we don't want to specify.
204 self.cmd
205 .find(n)
206 .map(|a| !a.is_hide_set())
207 .unwrap_or_default()
208 })
209 .filter(|key| !conflicting_keys.contains(key))
210 .cloned()
211 .collect();
212 let required: Vec<Id> = used_filtered
213 .iter()
214 .filter_map(|key| self.cmd.find(key))
215 .flat_map(|arg| arg.requires.iter().map(|item| &item.1))
216 .filter(|key| !used_filtered.contains(key) && !conflicting_keys.contains(key))
217 .chain(used_filtered.iter())
218 .cloned()
219 .collect();
220 Usage::new(self.cmd)
221 .required(&self.required)
222 .create_usage_with_title(&required)
223 }
224
225 fn gather_requires(&mut self, matcher: &ArgMatcher) {
226 debug!("Validator::gather_requires");
227 for (name, matched) in matcher
228 .args()
229 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
230 {
231 debug!("Validator::gather_requires:iter:{name:?}");
232 if let Some(arg) = self.cmd.find(name) {
233 let is_relevant = |(val, req_arg): &(ArgPredicate, Id)| -> Option<Id> {
234 let required = matched.check_explicit(val);
235 required.then(|| req_arg.clone())
236 };
237
238 for req in self.cmd.unroll_arg_requires(is_relevant, arg.get_id()) {
239 self.required.insert(req);
240 }
241 } else if let Some(g) = self.cmd.find_group(name) {
242 debug!("Validator::gather_requires:iter:{name:?}:group");
243 for r in &g.requires {
244 self.required.insert(r.clone());
245 }
246 }
247 }
248 }
249
250 fn validate_required(&mut self, matcher: &ArgMatcher, conflicts: &Conflicts) -> ClapResult<()> {
251 debug!("Validator::validate_required: required={:?}", self.required);
252 self.gather_requires(matcher);
253
254 let mut missing_required = Vec::new();
255 let mut highest_index = 0;
256
257 let is_exclusive_present = matcher
258 .args()
259 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
260 .any(|(id, _)| {
261 self.cmd
262 .find(id)
263 .map(|arg| arg.is_exclusive_set())
264 .unwrap_or_default()
265 });
266 debug!("Validator::validate_required: is_exclusive_present={is_exclusive_present}");
267
268 for arg_or_group in self
269 .required
270 .iter()
271 .filter(|r| !matcher.check_explicit(r, &ArgPredicate::IsPresent))
272 {
273 debug!("Validator::validate_required:iter:aog={arg_or_group:?}");
274 if let Some(arg) = self.cmd.find(arg_or_group) {
275 debug!("Validator::validate_required:iter: This is an arg");
276 if !is_exclusive_present && !self.is_missing_required_ok(arg, conflicts) {
277 debug!(
278 "Validator::validate_required:iter: Missing {:?}",
279 arg.get_id()
280 );
281 missing_required.push(arg.get_id().clone());
282 if !arg.is_last_set() {
283 highest_index = highest_index.max(arg.get_index().unwrap_or(0));
284 }
285 }
286 } else if let Some(group) = self.cmd.find_group(arg_or_group) {
287 debug!("Validator::validate_required:iter: This is a group");
288 if !self
289 .cmd
290 .unroll_args_in_group(&group.id)
291 .iter()
292 .any(|a| matcher.check_explicit(a, &ArgPredicate::IsPresent))
293 {
294 debug!(
295 "Validator::validate_required:iter: Missing {:?}",
296 group.get_id()
297 );
298 missing_required.push(group.get_id().clone());
299 }
300 }
301 }
302
303 // Validate the conditionally required args
304 for a in self
305 .cmd
306 .get_arguments()
307 .filter(|a| !matcher.check_explicit(a.get_id(), &ArgPredicate::IsPresent))
308 {
309 let mut required = false;
310
311 for (other, val) in &a.r_ifs {
312 if matcher.check_explicit(other, &ArgPredicate::Equals(val.into())) {
313 debug!(
314 "Validator::validate_required:iter: Missing {:?}",
315 a.get_id()
316 );
317 required = true;
318 }
319 }
320
321 let match_all = a.r_ifs_all.iter().all(|(other, val)| {
322 matcher.check_explicit(other, &ArgPredicate::Equals(val.into()))
323 });
324 if match_all && !a.r_ifs_all.is_empty() {
325 debug!(
326 "Validator::validate_required:iter: Missing {:?}",
327 a.get_id()
328 );
329 required = true;
330 }
331
332 if (!a.r_unless.is_empty() || !a.r_unless_all.is_empty())
333 && self.fails_arg_required_unless(a, matcher)
334 {
335 debug!(
336 "Validator::validate_required:iter: Missing {:?}",
337 a.get_id()
338 );
339 required = true;
340 }
341
342 if required {
343 missing_required.push(a.get_id().clone());
344 if !a.is_last_set() {
345 highest_index = highest_index.max(a.get_index().unwrap_or(0));
346 }
347 }
348 }
349
350 // For display purposes, include all of the preceding positional arguments
351 if !self.cmd.is_allow_missing_positional_set() {
352 for pos in self
353 .cmd
354 .get_positionals()
355 .filter(|a| !matcher.check_explicit(a.get_id(), &ArgPredicate::IsPresent))
356 {
357 if pos.get_index() < Some(highest_index) {
358 debug!(
359 "Validator::validate_required:iter: Missing {:?}",
360 pos.get_id()
361 );
362 missing_required.push(pos.get_id().clone());
363 }
364 }
365 }
366
367 if !missing_required.is_empty() {
368 ok!(self.missing_required_error(matcher, missing_required));
369 }
370
371 Ok(())
372 }
373
374 fn is_missing_required_ok(&self, a: &Arg, conflicts: &Conflicts) -> bool {
375 debug!("Validator::is_missing_required_ok: {}", a.get_id());
376 if !conflicts.gather_conflicts(self.cmd, a.get_id()).is_empty() {
377 debug!("Validator::is_missing_required_ok: true (self)");
378 return true;
379 }
380 for group_id in self.cmd.groups_for_arg(a.get_id()) {
381 if !conflicts.gather_conflicts(self.cmd, &group_id).is_empty() {
382 debug!("Validator::is_missing_required_ok: true ({group_id})");
383 return true;
384 }
385 }
386 false
387 }
388
389 // Failing a required unless means, the arg's "unless" wasn't present, and neither were they
390 fn fails_arg_required_unless(&self, a: &Arg, matcher: &ArgMatcher) -> bool {
391 debug!("Validator::fails_arg_required_unless: a={:?}", a.get_id());
392 let exists = |id| matcher.check_explicit(id, &ArgPredicate::IsPresent);
393
394 (a.r_unless_all.is_empty() || !a.r_unless_all.iter().all(exists))
395 && !a.r_unless.iter().any(exists)
396 }
397
398 // `req_args`: an arg to include in the error even if not used
399 fn missing_required_error(
400 &self,
401 matcher: &ArgMatcher,
402 raw_req_args: Vec<Id>,
403 ) -> ClapResult<()> {
404 debug!("Validator::missing_required_error; incl={raw_req_args:?}");
405 debug!(
406 "Validator::missing_required_error: reqs={:?}",
407 self.required
408 );
409
410 let usg = Usage::new(self.cmd).required(&self.required);
411
412 let req_args = {
413 #[cfg(feature = "usage")]
414 {
415 usg.get_required_usage_from(&raw_req_args, Some(matcher), true)
416 .into_iter()
417 .map(|s| s.to_string())
418 .collect::<Vec<_>>()
419 }
420
421 #[cfg(not(feature = "usage"))]
422 {
423 raw_req_args
424 .iter()
425 .map(|id| {
426 if let Some(arg) = self.cmd.find(id) {
427 arg.to_string()
428 } else if let Some(_group) = self.cmd.find_group(id) {
429 self.cmd.format_group(id).to_string()
430 } else {
431 debug_assert!(false, "id={id:?} is unknown");
432 "".to_owned()
433 }
434 })
435 .collect::<Vec<_>>()
436 }
437 };
438
439 debug!("Validator::missing_required_error: req_args={req_args:#?}");
440
441 let used: Vec<Id> = matcher
442 .args()
443 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
444 .map(|(n, _)| n)
445 .filter(|n| {
446 // Filter out the args we don't want to specify.
447 self.cmd
448 .find(n)
449 .map(|a| !a.is_hide_set())
450 .unwrap_or_default()
451 })
452 .cloned()
453 .chain(raw_req_args)
454 .collect();
455
456 Err(Error::missing_required_argument(
457 self.cmd,
458 req_args,
459 usg.create_usage_with_title(&used),
460 ))
461 }
462}
463
464#[derive(Default, Clone, Debug)]
465struct Conflicts {
466 potential: FlatMap<Id, Vec<Id>>,
467}
468
469impl Conflicts {
470 fn with_args(cmd: &Command, matcher: &ArgMatcher) -> Self {
471 let mut potential = FlatMap::new();
472 potential.extend_unchecked(
473 matcher
474 .args()
475 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
476 .map(|(id, _)| {
477 let conf = gather_direct_conflicts(cmd, id);
478 (id.clone(), conf)
479 }),
480 );
481 Self { potential }
482 }
483
484 fn gather_conflicts(&self, cmd: &Command, arg_id: &Id) -> Vec<Id> {
485 debug!("Conflicts::gather_conflicts: arg={arg_id:?}");
486 let mut conflicts = Vec::new();
487
488 let arg_id_conflicts_storage;
489 let arg_id_conflicts = if let Some(arg_id_conflicts) = self.get_direct_conflicts(arg_id) {
490 arg_id_conflicts
491 } else {
492 // `is_missing_required_ok` is a case where we check not-present args for conflicts
493 arg_id_conflicts_storage = gather_direct_conflicts(cmd, arg_id);
494 &arg_id_conflicts_storage
495 };
496 for (other_arg_id, other_arg_id_conflicts) in self.potential.iter() {
497 if arg_id == other_arg_id {
498 continue;
499 }
500
501 if arg_id_conflicts.contains(other_arg_id) {
502 conflicts.push(other_arg_id.clone());
503 }
504 if other_arg_id_conflicts.contains(arg_id) {
505 conflicts.push(other_arg_id.clone());
506 }
507 }
508
509 debug!("Conflicts::gather_conflicts: conflicts={conflicts:?}");
510 conflicts
511 }
512
513 fn get_direct_conflicts(&self, arg_id: &Id) -> Option<&[Id]> {
514 self.potential.get(arg_id).map(Vec::as_slice)
515 }
516}
517
518fn gather_direct_conflicts(cmd: &Command, id: &Id) -> Vec<Id> {
519 let conf: Vec = if let Some(arg: &Arg) = cmd.find(arg_id:id) {
520 gather_arg_direct_conflicts(cmd, arg)
521 } else if let Some(group: &ArgGroup) = cmd.find_group(group_id:id) {
522 gather_group_direct_conflicts(group)
523 } else {
524 debug_assert!(false, "id={id:?} is unknown");
525 Vec::new()
526 };
527 debug!("Conflicts::gather_direct_conflicts id={id:?}, conflicts={conf:?}",);
528 conf
529}
530
531fn gather_arg_direct_conflicts(cmd: &Command, arg: &Arg) -> Vec<Id> {
532 let mut conf: Vec = arg.blacklist.clone();
533 for group_id: Id in cmd.groups_for_arg(arg.get_id()) {
534 let group: &ArgGroup = cmd.find_group(&group_id).expect(INTERNAL_ERROR_MSG);
535 conf.extend(iter:group.conflicts.iter().cloned());
536 if !group.multiple {
537 for member_id: &Id in &group.args {
538 if member_id != arg.get_id() {
539 conf.push(member_id.clone());
540 }
541 }
542 }
543 }
544
545 // Overrides are implicitly conflicts
546 conf.extend(iter:arg.overrides.iter().cloned());
547
548 conf
549}
550
551fn gather_group_direct_conflicts(group: &ArgGroup) -> Vec<Id> {
552 group.conflicts.clone()
553}
554
555pub(crate) fn get_possible_values_cli(a: &Arg) -> Vec<PossibleValue> {
556 if !a.is_takes_value_set() {
557 vec![]
558 } else {
559 aOption>.get_value_parser()
560 .possible_values()
561 .map(|pvs: Box>| pvs.collect())
562 .unwrap_or_default()
563 }
564}
565