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
;
11 use crate::INTERNAL_ERROR_MSG
;
13 pub(crate) struct Validator
<'cmd
> {
15 required
: ChildGraph
<Id
>,
18 impl<'cmd
> Validator
<'cmd
> {
19 pub(crate) fn new(cmd
: &'cmd Command
) -> Self {
20 let required
= cmd
.required_graph();
21 Validator { cmd, required }
24 pub(crate) fn validate(
26 parse_state
: ParseState
,
27 matcher
: &mut ArgMatcher
,
29 debug
!("Validator::validate");
30 let conflicts
= Conflicts
::with_args(self.cmd
, matcher
);
31 let has_subcmd
= matcher
.subcommand_name().is_some();
33 if let ParseState
::Opt(a
) = parse_state
{
34 debug
!("Validator::validate: needs_val_of={:?}", a
);
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
43 return Err(Error
::empty_value(
45 &get_possible_values_cli(o
)
47 .filter(|pv
| !pv
.is_hide_set())
48 .map(|n
| n
.get_name().to_owned())
55 if !has_subcmd
&& self.cmd
.is_arg_required_else_help_set() {
56 let num_user_values
= matcher
58 .filter(|(_
, matched
)| matched
.check_explicit(&ArgPredicate
::IsPresent
))
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
));
65 if !has_subcmd
&& self.cmd
.is_subcommand_required_set() {
69 .unwrap_or_else(|| self.cmd
.get_name());
70 return Err(Error
::missing_subcommand(
74 .all_subcommand_names()
75 .map(|s
| s
.to_owned())
78 .required(&self.required
)
79 .create_usage_with_title(&[]),
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
));
91 fn validate_conflicts(
94 conflicts
: &Conflicts
,
96 debug
!("Validator::validate_conflicts");
98 ok
!(self.validate_exclusive(matcher
));
100 for (arg_id
, _
) in matcher
102 .filter(|(_
, matched
)| matched
.check_explicit(&ArgPredicate
::IsPresent
))
103 .filter(|(arg_id
, _
)| self.cmd
.find(arg_id
).is_some())
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
));
113 fn validate_exclusive(&self, matcher
: &ArgMatcher
) -> ClapResult
<()> {
114 debug
!("Validator::validate_exclusive");
115 let args_count
= matcher
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()
125 // Nothing present to conflict with
131 .filter(|(_
, matched
)| matched
.check_explicit(&crate::builder
::ArgPredicate
::IsPresent
))
132 .filter_map(|(id
, _
)| {
133 debug
!("Validator::validate_exclusive:iter:{:?}", id
);
136 // Find `arg`s which are exclusive but also appear with other args.
137 .filter(|&arg
| arg
.is_exclusive_set() && args_count
> 1)
139 // Throw an error for the first conflict found.
140 .try_for_each(|arg
| {
141 Err(Error
::argument_conflict(
146 .required(&self.required
)
147 .create_usage_with_title(&[]),
152 fn build_conflict_err(
156 matcher
: &ArgMatcher
,
157 ) -> ClapResult
<()> {
158 if conflict_ids
.is_empty() {
162 debug
!("Validator::build_conflict_err: name={:?}", name
);
163 let mut seen
= FlatSet
::new();
164 let conflicts
= conflict_ids
167 if self.cmd
.find_group(c_id
).is_some() {
168 self.cmd
.unroll_args_in_group(c_id
)
174 seen
.insert(c_id
.clone()).then(|| {
175 let c_arg
= self.cmd
.find(&c_id
).expect(INTERNAL_ERROR_MSG
);
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(
185 former_arg
.to_string(),
191 fn build_conflict_err_usage(
193 matcher
: &ArgMatcher
,
194 conflicting_keys
: &[Id
],
195 ) -> Option
<StyledStr
> {
196 let used_filtered
: Vec
<Id
> = matcher
198 .filter(|(_
, matched
)| matched
.check_explicit(&ArgPredicate
::IsPresent
))
201 // Filter out the args we don't want to specify.
202 self.cmd
.find(n
).map_or(false, |a
| !a
.is_hide_set())
204 .filter(|key
| !conflicting_keys
.contains(key
))
207 let required
: Vec
<Id
> = used_filtered
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())
216 .required(&self.required
)
217 .create_usage_with_title(&required
)
220 fn gather_requires(&mut self, matcher
: &ArgMatcher
) {
221 debug
!("Validator::gather_requires");
222 for (name
, matched
) in matcher
224 .filter(|(_
, matched
)| matched
.check_explicit(&ArgPredicate
::IsPresent
))
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())
233 for req
in self.cmd
.unroll_arg_requires(is_relevant
, arg
.get_id()) {
234 self.required
.insert(req
);
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());
245 fn validate_required(&mut self, matcher
: &ArgMatcher
, conflicts
: &Conflicts
) -> ClapResult
<()> {
246 debug
!("Validator::validate_required: required={:?}", self.required
);
247 self.gather_requires(matcher
);
249 let mut missing_required
= Vec
::new();
250 let mut highest_index
= 0;
252 let is_exclusive_present
= matcher
254 .filter(|(_
, matched
)| matched
.check_explicit(&ArgPredicate
::IsPresent
))
258 .map(|arg
| arg
.is_exclusive_set())
262 "Validator::validate_required: is_exclusive_present={}",
266 for arg_or_group
in self
269 .filter(|r
| !matcher
.check_explicit(r
, &ArgPredicate
::IsPresent
))
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
) {
276 "Validator::validate_required:iter: Missing {:?}",
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));
284 } else if let Some(group
) = self.cmd
.find_group(arg_or_group
) {
285 debug
!("Validator::validate_required:iter: This is a group");
288 .unroll_args_in_group(&group
.id
)
290 .any(|a
| matcher
.check_explicit(a
, &ArgPredicate
::IsPresent
))
293 "Validator::validate_required:iter: Missing {:?}",
296 missing_required
.push(group
.get_id().clone());
301 // Validate the conditionally required args
305 .filter(|a
| !matcher
.check_explicit(a
.get_id(), &ArgPredicate
::IsPresent
))
307 let mut required
= false;
309 for (other
, val
) in &a
.r_ifs
{
310 if matcher
.check_explicit(other
, &ArgPredicate
::Equals(val
.into())) {
312 "Validator::validate_required:iter: Missing {:?}",
319 let match_all
= a
.r_ifs_all
.iter().all(|(other
, val
)| {
320 matcher
.check_explicit(other
, &ArgPredicate
::Equals(val
.into()))
322 if match_all
&& !a
.r_ifs_all
.is_empty() {
324 "Validator::validate_required:iter: Missing {:?}",
330 if (!a
.r_unless
.is_empty() || !a
.r_unless_all
.is_empty())
331 && self.fails_arg_required_unless(a
, matcher
)
334 "Validator::validate_required:iter: Missing {:?}",
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));
348 // For display purposes, include all of the preceding positional arguments
349 if !self.cmd
.is_allow_missing_positional_set() {
353 .filter(|a
| !matcher
.check_explicit(a
.get_id(), &ArgPredicate
::IsPresent
))
355 if pos
.get_index() < Some(highest_index
) {
357 "Validator::validate_required:iter: Missing {:?}",
360 missing_required
.push(pos
.get_id().clone());
365 if !missing_required
.is_empty() {
366 ok
!(self.missing_required_error(matcher
, missing_required
));
372 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)");
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
);
387 // Failing a required unless means, the arg's "unless" wasn't present, and neither were they
388 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
);
392 (a
.r_unless_all
.is_empty() || !a
.r_unless_all
.iter().all(exists
))
393 && !a
.r_unless
.iter().any(exists
)
396 // `req_args`: an arg to include in the error even if not used
397 fn missing_required_error(
399 matcher
: &ArgMatcher
,
400 raw_req_args
: Vec
<Id
>,
401 ) -> ClapResult
<()> {
402 debug
!("Validator::missing_required_error; incl={:?}", raw_req_args
);
404 "Validator::missing_required_error: reqs={:?}",
408 let usg
= Usage
::new(self.cmd
).required(&self.required
);
411 #[cfg(feature = "usage")]
413 usg
.get_required_usage_from(&raw_req_args
, Some(matcher
), true)
415 .map(|s
| s
.to_string())
419 #[cfg(not(feature = "usage"))]
424 if let Some(arg
) = self.cmd
.find(id
) {
426 } else if let Some(_group
) = self.cmd
.find_group(id
) {
427 self.cmd
.format_group(id
).to_string()
429 debug_assert
!(false, "id={:?} is unknown", id
);
438 "Validator::missing_required_error: req_args={:#?}",
442 let used
: Vec
<Id
> = matcher
444 .filter(|(_
, matched
)| matched
.check_explicit(&ArgPredicate
::IsPresent
))
447 // Filter out the args we don't want to specify.
448 self.cmd
.find(n
).map_or(false, |a
| !a
.is_hide_set())
454 Err(Error
::missing_required_argument(
457 usg
.create_usage_with_title(&used
),
462 #[derive(Default, Clone, Debug)]
464 potential
: FlatMap
<Id
, Vec
<Id
>>,
468 fn with_args(cmd
: &Command
, matcher
: &ArgMatcher
) -> Self {
469 let mut potential
= FlatMap
::new();
470 potential
.extend_unchecked(
473 .filter(|(_
, matched
)| matched
.check_explicit(&ArgPredicate
::IsPresent
))
475 let conf
= gather_direct_conflicts(cmd
, 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();
486 let arg_id_conflicts_storage
;
487 let arg_id_conflicts
= if let Some(arg_id_conflicts
) = self.get_direct_conflicts(arg_id
) {
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
494 for (other_arg_id
, other_arg_id_conflicts
) in self.potential
.iter() {
495 if arg_id
== other_arg_id
{
499 if arg_id_conflicts
.contains(other_arg_id
) {
500 conflicts
.push(other_arg_id
.clone());
502 if other_arg_id_conflicts
.contains(arg_id
) {
503 conflicts
.push(other_arg_id
.clone());
507 debug
!("Conflicts::gather_conflicts: conflicts={:?}", conflicts
);
511 fn get_direct_conflicts(&self, arg_id
: &Id
) -> Option
<&[Id
]> {
512 self.potential
.get(arg_id
).map(Vec
::as_slice
)
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
)
522 debug_assert
!(false, "id={:?} is unknown", id
);
526 "Conflicts::gather_direct_conflicts id={:?}, conflicts={:?}",
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());
538 for member_id
in &group
.args
{
539 if member_id
!= arg
.get_id() {
540 conf
.push(member_id
.clone());
546 // Overrides are implicitly conflicts
547 conf
.extend(arg
.overrides
.iter().cloned());
552 fn gather_group_direct_conflicts(group
: &ArgGroup
) -> Vec
<Id
> {
553 group
.conflicts
.clone()
556 pub(crate) fn get_possible_values_cli(a
: &Arg
) -> Vec
<PossibleValue
> {
557 if !a
.is_takes_value_set() {
562 .map(|pvs
| pvs
.collect())