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