1use 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.cmd.get_bin_name_fallback();
67 return Err(Error::missing_subcommand(
68 self.cmd,
69 bn.to_string(),
70 self.cmd
71 .all_subcommand_names()
72 .map(|s| s.to_owned())
73 .collect::<Vec<_>>(),
74 Usage::new(self.cmd)
75 .required(&self.required)
76 .create_usage_with_title(&[]),
77 ));
78 }
79
80 ok!(self.validate_conflicts(matcher, &conflicts));
81 if !(self.cmd.is_subcommand_negates_reqs_set() && has_subcmd) {
82 ok!(self.validate_required(matcher, &conflicts));
83 }
84
85 Ok(())
86 }
87
88 fn validate_conflicts(
89 &mut self,
90 matcher: &ArgMatcher,
91 conflicts: &Conflicts,
92 ) -> ClapResult<()> {
93 debug!("Validator::validate_conflicts");
94
95 ok!(self.validate_exclusive(matcher));
96
97 for (arg_id, _) in matcher
98 .args()
99 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
100 .filter(|(arg_id, _)| self.cmd.find(arg_id).is_some())
101 {
102 debug!("Validator::validate_conflicts::iter: id={arg_id:?}");
103 let conflicts = conflicts.gather_conflicts(self.cmd, arg_id);
104 ok!(self.build_conflict_err(arg_id, &conflicts, matcher));
105 }
106
107 Ok(())
108 }
109
110 fn validate_exclusive(&self, matcher: &ArgMatcher) -> ClapResult<()> {
111 debug!("Validator::validate_exclusive");
112 let args_count = matcher
113 .args()
114 .filter(|(arg_id, matched)| {
115 matched.check_explicit(&ArgPredicate::IsPresent)
116 && self.cmd.find(arg_id).is_some()
119 })
120 .count();
121 if args_count <= 1 {
122 return Ok(());
124 }
125
126 matcher
127 .args()
128 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
129 .find_map(|(id, _)| {
130 debug!("Validator::validate_exclusive:iter:{id:?}");
131 self.cmd
132 .find(id)
133 .filter(|&arg| arg.is_exclusive_set() && args_count > 1)
135 })
136 .map(|arg| {
137 Err(Error::argument_conflict(
139 self.cmd,
140 arg.to_string(),
141 Vec::new(),
142 Usage::new(self.cmd)
143 .required(&self.required)
144 .create_usage_with_title(&[]),
145 ))
146 })
147 .unwrap_or(Ok(()))
148 }
149
150 fn build_conflict_err(
151 &self,
152 name: &Id,
153 conflict_ids: &[Id],
154 matcher: &ArgMatcher,
155 ) -> ClapResult<()> {
156 if conflict_ids.is_empty() {
157 return Ok(());
158 }
159
160 debug!("Validator::build_conflict_err: name={name:?}");
161 let mut seen = FlatSet::new();
162 let conflicts = conflict_ids
163 .iter()
164 .flat_map(|c_id| {
165 if self.cmd.find_group(c_id).is_some() {
166 self.cmd.unroll_args_in_group(c_id)
167 } else {
168 vec![c_id.clone()]
169 }
170 })
171 .filter_map(|c_id| {
172 seen.insert(c_id.clone()).then(|| {
173 let c_arg = self.cmd.find(&c_id).expect(INTERNAL_ERROR_MSG);
174 c_arg.to_string()
175 })
176 })
177 .collect();
178
179 let former_arg = self.cmd.find(name).expect(INTERNAL_ERROR_MSG);
180 let usg = self.build_conflict_err_usage(matcher, conflict_ids);
181 Err(Error::argument_conflict(
182 self.cmd,
183 former_arg.to_string(),
184 conflicts,
185 usg,
186 ))
187 }
188
189 fn build_conflict_err_usage(
190 &self,
191 matcher: &ArgMatcher,
192 conflicting_keys: &[Id],
193 ) -> Option<StyledStr> {
194 let used_filtered: Vec<Id> = matcher
195 .args()
196 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
197 .map(|(n, _)| n)
198 .filter(|n| {
199 self.cmd
201 .find(n)
202 .map(|a| !a.is_hide_set())
203 .unwrap_or_default()
204 })
205 .filter(|key| !conflicting_keys.contains(key))
206 .cloned()
207 .collect();
208 let required: Vec<Id> = used_filtered
209 .iter()
210 .filter_map(|key| self.cmd.find(key))
211 .flat_map(|arg| arg.requires.iter().map(|item| &item.1))
212 .filter(|key| !used_filtered.contains(key) && !conflicting_keys.contains(key))
213 .chain(used_filtered.iter())
214 .cloned()
215 .collect();
216 Usage::new(self.cmd)
217 .required(&self.required)
218 .create_usage_with_title(&required)
219 }
220
221 fn gather_requires(&mut self, matcher: &ArgMatcher) {
222 debug!("Validator::gather_requires");
223 for (name, matched) in matcher
224 .args()
225 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
226 {
227 debug!("Validator::gather_requires:iter:{name:?}");
228 if let Some(arg) = self.cmd.find(name) {
229 let is_relevant = |(val, req_arg): &(ArgPredicate, Id)| -> Option<Id> {
230 let required = matched.check_explicit(val);
231 required.then(|| req_arg.clone())
232 };
233
234 for req in self.cmd.unroll_arg_requires(is_relevant, arg.get_id()) {
235 self.required.insert(req);
236 }
237 } else if let Some(g) = self.cmd.find_group(name) {
238 debug!("Validator::gather_requires:iter:{name:?}:group");
239 for r in &g.requires {
240 self.required.insert(r.clone());
241 }
242 }
243 }
244 }
245
246 fn validate_required(&mut self, matcher: &ArgMatcher, conflicts: &Conflicts) -> ClapResult<()> {
247 debug!("Validator::validate_required: required={:?}", self.required);
248 self.gather_requires(matcher);
249
250 let mut missing_required = Vec::new();
251 let mut highest_index = 0;
252
253 let is_exclusive_present = matcher
254 .args()
255 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
256 .any(|(id, _)| {
257 self.cmd
258 .find(id)
259 .map(|arg| arg.is_exclusive_set())
260 .unwrap_or_default()
261 });
262 debug!("Validator::validate_required: is_exclusive_present={is_exclusive_present}");
263
264 for arg_or_group in self
265 .required
266 .iter()
267 .filter(|r| !matcher.check_explicit(r, &ArgPredicate::IsPresent))
268 {
269 debug!("Validator::validate_required:iter:aog={arg_or_group:?}");
270 if let Some(arg) = self.cmd.find(arg_or_group) {
271 debug!("Validator::validate_required:iter: This is an arg");
272 if !is_exclusive_present && !self.is_missing_required_ok(arg, conflicts) {
273 debug!(
274 "Validator::validate_required:iter: Missing {:?}",
275 arg.get_id()
276 );
277 missing_required.push(arg.get_id().clone());
278 if !arg.is_last_set() {
279 highest_index = highest_index.max(arg.get_index().unwrap_or(0));
280 }
281 }
282 } else if let Some(group) = self.cmd.find_group(arg_or_group) {
283 debug!("Validator::validate_required:iter: This is a group");
284 if !self
285 .cmd
286 .unroll_args_in_group(&group.id)
287 .iter()
288 .any(|a| matcher.check_explicit(a, &ArgPredicate::IsPresent))
289 {
290 debug!(
291 "Validator::validate_required:iter: Missing {:?}",
292 group.get_id()
293 );
294 missing_required.push(group.get_id().clone());
295 }
296 }
297 }
298
299 for a in self
301 .cmd
302 .get_arguments()
303 .filter(|a| !matcher.check_explicit(a.get_id(), &ArgPredicate::IsPresent))
304 {
305 let mut required = false;
306
307 for (other, val) in &a.r_ifs {
308 if matcher.check_explicit(other, &ArgPredicate::Equals(val.into())) {
309 debug!(
310 "Validator::validate_required:iter: Missing {:?}",
311 a.get_id()
312 );
313 required = true;
314 }
315 }
316
317 let match_all = a.r_ifs_all.iter().all(|(other, val)| {
318 matcher.check_explicit(other, &ArgPredicate::Equals(val.into()))
319 });
320 if match_all && !a.r_ifs_all.is_empty() {
321 debug!(
322 "Validator::validate_required:iter: Missing {:?}",
323 a.get_id()
324 );
325 required = true;
326 }
327
328 if (!a.r_unless.is_empty() || !a.r_unless_all.is_empty())
329 && self.fails_arg_required_unless(a, matcher)
330 {
331 debug!(
332 "Validator::validate_required:iter: Missing {:?}",
333 a.get_id()
334 );
335 required = true;
336 }
337
338 if !is_exclusive_present && required {
339 missing_required.push(a.get_id().clone());
340 if !a.is_last_set() {
341 highest_index = highest_index.max(a.get_index().unwrap_or(0));
342 }
343 }
344 }
345
346 if !self.cmd.is_allow_missing_positional_set() {
348 for pos in self
349 .cmd
350 .get_positionals()
351 .filter(|a| !matcher.check_explicit(a.get_id(), &ArgPredicate::IsPresent))
352 {
353 if pos.get_index() < Some(highest_index) {
354 debug!(
355 "Validator::validate_required:iter: Missing {:?}",
356 pos.get_id()
357 );
358 missing_required.push(pos.get_id().clone());
359 }
360 }
361 }
362
363 if !missing_required.is_empty() {
364 ok!(self.missing_required_error(matcher, missing_required));
365 }
366
367 Ok(())
368 }
369
370 fn is_missing_required_ok(&self, a: &Arg, conflicts: &Conflicts) -> bool {
371 debug!("Validator::is_missing_required_ok: {}", a.get_id());
372 if !conflicts.gather_conflicts(self.cmd, a.get_id()).is_empty() {
373 debug!("Validator::is_missing_required_ok: true (self)");
374 return true;
375 }
376 for group_id in self.cmd.groups_for_arg(a.get_id()) {
377 if !conflicts.gather_conflicts(self.cmd, &group_id).is_empty() {
378 debug!("Validator::is_missing_required_ok: true ({group_id})");
379 return true;
380 }
381 }
382 false
383 }
384
385 fn fails_arg_required_unless(&self, a: &Arg, matcher: &ArgMatcher) -> bool {
387 debug!("Validator::fails_arg_required_unless: a={:?}", a.get_id());
388 let exists = |id| matcher.check_explicit(id, &ArgPredicate::IsPresent);
389
390 (a.r_unless_all.is_empty() || !a.r_unless_all.iter().all(exists))
391 && !a.r_unless.iter().any(exists)
392 }
393
394 fn missing_required_error(
396 &self,
397 matcher: &ArgMatcher,
398 raw_req_args: Vec<Id>,
399 ) -> ClapResult<()> {
400 debug!("Validator::missing_required_error; incl={raw_req_args:?}");
401 debug!(
402 "Validator::missing_required_error: reqs={:?}",
403 self.required
404 );
405
406 let usg = Usage::new(self.cmd).required(&self.required);
407
408 let req_args = {
409 #[cfg(feature = "usage")]
410 {
411 usg.get_required_usage_from(&raw_req_args, Some(matcher), true)
412 .into_iter()
413 .map(|s| s.to_string())
414 .collect::<Vec<_>>()
415 }
416
417 #[cfg(not(feature = "usage"))]
418 {
419 raw_req_args
420 .iter()
421 .map(|id| {
422 if let Some(arg) = self.cmd.find(id) {
423 arg.to_string()
424 } else if let Some(_group) = self.cmd.find_group(id) {
425 self.cmd.format_group(id).to_string()
426 } else {
427 debug_assert!(false, "id={id:?} is unknown");
428 "".to_owned()
429 }
430 })
431 .collect::<FlatSet<_>>()
432 .into_iter()
433 .collect::<Vec<_>>()
434 }
435 };
436
437 debug!("Validator::missing_required_error: req_args={req_args:#?}");
438
439 let used: Vec<Id> = matcher
440 .args()
441 .filter(|(_, matched)| matched.check_explicit(&ArgPredicate::IsPresent))
442 .map(|(n, _)| n)
443 .filter(|n| {
444 self.cmd
446 .find(n)
447 .map(|a| !a.is_hide_set())
448 .unwrap_or_default()
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)]
463struct Conflicts {
464 potential: FlatMap<Id, Vec<Id>>,
465}
466
467impl Conflicts {
468 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
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 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
511 fn get_direct_conflicts(&self, arg_id: &Id) -> Option<&[Id]> {
512 self.potential.get(arg_id).map(Vec::as_slice)
513 }
514}
515
516fn 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={id:?} is unknown");
523 Vec::new()
524 };
525 debug!("Conflicts::gather_direct_conflicts id={id:?}, conflicts={conf:?}",);
526 conf
527}
528
529fn gather_arg_direct_conflicts(cmd: &Command, arg: &Arg) -> Vec<Id> {
530 let mut conf = arg.blacklist.clone();
531 for group_id in cmd.groups_for_arg(arg.get_id()) {
532 let group = cmd.find_group(&group_id).expect(INTERNAL_ERROR_MSG);
533 conf.extend(group.conflicts.iter().cloned());
534 if !group.multiple {
535 for member_id in &group.args {
536 if member_id != arg.get_id() {
537 conf.push(member_id.clone());
538 }
539 }
540 }
541 }
542
543 conf.extend(arg.overrides.iter().cloned());
545
546 conf
547}
548
549fn gather_group_direct_conflicts(group: &ArgGroup) -> Vec<Id> {
550 group.conflicts.clone()
551}
552
553pub(crate) fn get_possible_values_cli(a: &Arg) -> Vec<PossibleValue> {
554 if !a.is_takes_value_set() {
555 vec![]
556 } else {
557 a.get_value_parser()
558 .possible_values()
559 .map(|pvs| pvs.collect())
560 .unwrap_or_default()
561 }
562}