]>
Commit | Line | Data |
---|---|---|
04454e1e FG |
1 | // Internal |
2 | use crate::build::{AppSettings, Arg, ArgPredicate, Command, PossibleValue}; | |
3 | use crate::error::{Error, Result as ClapResult}; | |
4 | use crate::output::Usage; | |
5 | use crate::parse::{ArgMatcher, MatchedArg, ParseState, Parser}; | |
6 | use crate::util::ChildGraph; | |
7 | use crate::util::Id; | |
8 | use crate::{INTERNAL_ERROR_MSG, INVALID_UTF8}; | |
9 | ||
10 | pub(crate) struct Validator<'help, 'cmd, 'parser> { | |
11 | p: &'parser mut Parser<'help, 'cmd>, | |
12 | required: ChildGraph<Id>, | |
13 | } | |
14 | ||
15 | impl<'help, 'cmd, 'parser> Validator<'help, 'cmd, 'parser> { | |
16 | pub(crate) fn new(p: &'parser mut Parser<'help, 'cmd>) -> Self { | |
17 | let required = p.cmd.required_graph(); | |
18 | Validator { p, required } | |
19 | } | |
20 | ||
21 | pub(crate) fn validate( | |
22 | &mut self, | |
23 | parse_state: ParseState, | |
24 | matcher: &mut ArgMatcher, | |
25 | trailing_values: bool, | |
26 | ) -> ClapResult<()> { | |
27 | debug!("Validator::validate"); | |
28 | let has_subcmd = matcher.subcommand_name().is_some(); | |
29 | ||
30 | #[cfg(feature = "env")] | |
31 | self.p.add_env(matcher, trailing_values)?; | |
32 | ||
33 | self.p.add_defaults(matcher, trailing_values); | |
34 | ||
35 | if let ParseState::Opt(a) = parse_state { | |
36 | debug!("Validator::validate: needs_val_of={:?}", a); | |
37 | ||
38 | let o = &self.p.cmd[&a]; | |
39 | let should_err = if let Some(v) = matcher.args.get(&o.id) { | |
40 | v.all_val_groups_empty() && !(o.min_vals.is_some() && o.min_vals.unwrap() == 0) | |
41 | } else { | |
42 | true | |
43 | }; | |
44 | if should_err { | |
45 | return Err(Error::empty_value( | |
46 | self.p.cmd, | |
47 | &o.possible_vals | |
48 | .iter() | |
49 | .filter_map(PossibleValue::get_visible_name) | |
50 | .collect::<Vec<_>>(), | |
51 | o, | |
52 | Usage::new(self.p.cmd) | |
53 | .required(&self.required) | |
54 | .create_usage_with_title(&[]), | |
55 | )); | |
56 | } | |
57 | } | |
58 | ||
59 | if !has_subcmd && self.p.cmd.is_arg_required_else_help_set() { | |
60 | let num_user_values = matcher | |
61 | .arg_names() | |
62 | .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) | |
63 | .count(); | |
64 | if num_user_values == 0 { | |
65 | let message = self.p.write_help_err()?; | |
66 | return Err(Error::display_help_error(self.p.cmd, message)); | |
67 | } | |
68 | } | |
69 | #[allow(deprecated)] | |
70 | if !has_subcmd && self.p.cmd.is_subcommand_required_set() { | |
71 | let bn = self | |
72 | .p | |
73 | .cmd | |
74 | .get_bin_name() | |
75 | .unwrap_or_else(|| self.p.cmd.get_name()); | |
76 | return Err(Error::missing_subcommand( | |
77 | self.p.cmd, | |
78 | bn.to_string(), | |
79 | Usage::new(self.p.cmd) | |
80 | .required(&self.required) | |
81 | .create_usage_with_title(&[]), | |
82 | )); | |
83 | } else if !has_subcmd && self.p.cmd.is_set(AppSettings::SubcommandRequiredElseHelp) { | |
84 | debug!("Validator::new::get_matches_with: SubcommandRequiredElseHelp=true"); | |
85 | let message = self.p.write_help_err()?; | |
86 | return Err(Error::display_help_error(self.p.cmd, message)); | |
87 | } | |
88 | ||
89 | self.validate_conflicts(matcher)?; | |
90 | if !(self.p.cmd.is_subcommand_negates_reqs_set() && has_subcmd) { | |
91 | self.validate_required(matcher)?; | |
92 | } | |
93 | self.validate_matched_args(matcher)?; | |
94 | ||
95 | Ok(()) | |
96 | } | |
97 | ||
98 | fn validate_arg_values( | |
99 | &self, | |
100 | arg: &Arg, | |
101 | ma: &MatchedArg, | |
102 | matcher: &ArgMatcher, | |
103 | ) -> ClapResult<()> { | |
104 | debug!("Validator::validate_arg_values: arg={:?}", arg.name); | |
105 | for val in ma.vals_flatten() { | |
106 | if !arg.is_allow_invalid_utf8_set() && val.to_str().is_none() { | |
107 | debug!( | |
108 | "Validator::validate_arg_values: invalid UTF-8 found in val {:?}", | |
109 | val | |
110 | ); | |
111 | return Err(Error::invalid_utf8( | |
112 | self.p.cmd, | |
113 | Usage::new(self.p.cmd) | |
114 | .required(&self.required) | |
115 | .create_usage_with_title(&[]), | |
116 | )); | |
117 | } | |
118 | if !arg.possible_vals.is_empty() { | |
119 | debug!( | |
120 | "Validator::validate_arg_values: possible_vals={:?}", | |
121 | arg.possible_vals | |
122 | ); | |
123 | let val_str = val.to_string_lossy(); | |
124 | let ok = arg | |
125 | .possible_vals | |
126 | .iter() | |
127 | .any(|pv| pv.matches(&val_str, arg.is_ignore_case_set())); | |
128 | if !ok { | |
129 | let used: Vec<Id> = matcher | |
130 | .arg_names() | |
131 | .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) | |
132 | .filter(|&n| { | |
133 | self.p.cmd.find(n).map_or(true, |a| { | |
134 | !(a.is_hide_set() || self.required.contains(&a.id)) | |
135 | }) | |
136 | }) | |
137 | .cloned() | |
138 | .collect(); | |
139 | return Err(Error::invalid_value( | |
140 | self.p.cmd, | |
141 | val_str.into_owned(), | |
142 | &arg.possible_vals | |
143 | .iter() | |
144 | .filter_map(PossibleValue::get_visible_name) | |
145 | .collect::<Vec<_>>(), | |
146 | arg, | |
147 | Usage::new(self.p.cmd) | |
148 | .required(&self.required) | |
149 | .create_usage_with_title(&used), | |
150 | )); | |
151 | } | |
152 | } | |
153 | if arg.is_forbid_empty_values_set() && val.is_empty() && matcher.contains(&arg.id) { | |
154 | debug!("Validator::validate_arg_values: illegal empty val found"); | |
155 | return Err(Error::empty_value( | |
156 | self.p.cmd, | |
157 | &arg.possible_vals | |
158 | .iter() | |
159 | .filter_map(PossibleValue::get_visible_name) | |
160 | .collect::<Vec<_>>(), | |
161 | arg, | |
162 | Usage::new(self.p.cmd) | |
163 | .required(&self.required) | |
164 | .create_usage_with_title(&[]), | |
165 | )); | |
166 | } | |
167 | ||
168 | if let Some(ref vtor) = arg.validator { | |
169 | debug!("Validator::validate_arg_values: checking validator..."); | |
170 | let mut vtor = vtor.lock().unwrap(); | |
171 | if let Err(e) = vtor(&*val.to_string_lossy()) { | |
172 | debug!("error"); | |
173 | return Err(Error::value_validation( | |
174 | arg.to_string(), | |
175 | val.to_string_lossy().into_owned(), | |
176 | e, | |
177 | ) | |
178 | .with_cmd(self.p.cmd)); | |
179 | } else { | |
180 | debug!("good"); | |
181 | } | |
182 | } | |
183 | if let Some(ref vtor) = arg.validator_os { | |
184 | debug!("Validator::validate_arg_values: checking validator_os..."); | |
185 | let mut vtor = vtor.lock().unwrap(); | |
186 | if let Err(e) = vtor(val) { | |
187 | debug!("error"); | |
188 | return Err(Error::value_validation( | |
189 | arg.to_string(), | |
190 | val.to_string_lossy().into(), | |
191 | e, | |
192 | ) | |
193 | .with_cmd(self.p.cmd)); | |
194 | } else { | |
195 | debug!("good"); | |
196 | } | |
197 | } | |
198 | } | |
199 | Ok(()) | |
200 | } | |
201 | ||
202 | fn validate_conflicts(&self, matcher: &ArgMatcher) -> ClapResult<()> { | |
203 | debug!("Validator::validate_conflicts"); | |
204 | ||
205 | self.validate_exclusive(matcher)?; | |
206 | ||
207 | let mut conflicts = Conflicts::new(); | |
208 | for arg_id in matcher | |
209 | .arg_names() | |
210 | .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) | |
211 | .filter(|arg_id| self.p.cmd.find(arg_id).is_some()) | |
212 | { | |
213 | debug!("Validator::validate_conflicts::iter: id={:?}", arg_id); | |
214 | let conflicts = conflicts.gather_conflicts(self.p.cmd, matcher, arg_id); | |
215 | self.build_conflict_err(arg_id, &conflicts, matcher)?; | |
216 | } | |
217 | ||
218 | Ok(()) | |
219 | } | |
220 | ||
221 | fn validate_exclusive(&self, matcher: &ArgMatcher) -> ClapResult<()> { | |
222 | debug!("Validator::validate_exclusive"); | |
223 | // Not bothering to filter for `check_explicit` since defaults shouldn't play into this | |
224 | let args_count = matcher.arg_names().count(); | |
225 | matcher | |
226 | .arg_names() | |
227 | .filter_map(|name| { | |
228 | debug!("Validator::validate_exclusive:iter:{:?}", name); | |
229 | self.p | |
230 | .cmd | |
231 | .find(name) | |
232 | // Find `arg`s which are exclusive but also appear with other args. | |
233 | .filter(|&arg| arg.is_exclusive_set() && args_count > 1) | |
234 | }) | |
235 | // Throw an error for the first conflict found. | |
236 | .try_for_each(|arg| { | |
237 | Err(Error::argument_conflict( | |
238 | self.p.cmd, | |
239 | arg, | |
240 | Vec::new(), | |
241 | Usage::new(self.p.cmd) | |
242 | .required(&self.required) | |
243 | .create_usage_with_title(&[]), | |
244 | )) | |
245 | }) | |
246 | } | |
247 | ||
248 | fn build_conflict_err( | |
249 | &self, | |
250 | name: &Id, | |
251 | conflict_ids: &[Id], | |
252 | matcher: &ArgMatcher, | |
253 | ) -> ClapResult<()> { | |
254 | if conflict_ids.is_empty() { | |
255 | return Ok(()); | |
256 | } | |
257 | ||
258 | debug!("Validator::build_conflict_err: name={:?}", name); | |
259 | let mut seen = std::collections::HashSet::new(); | |
260 | let conflicts = conflict_ids | |
261 | .iter() | |
262 | .flat_map(|c_id| { | |
263 | if self.p.cmd.find_group(c_id).is_some() { | |
264 | self.p.cmd.unroll_args_in_group(c_id) | |
265 | } else { | |
266 | vec![c_id.clone()] | |
267 | } | |
268 | }) | |
269 | .filter_map(|c_id| { | |
270 | seen.insert(c_id.clone()).then(|| { | |
271 | let c_arg = self.p.cmd.find(&c_id).expect(INTERNAL_ERROR_MSG); | |
272 | c_arg.to_string() | |
273 | }) | |
274 | }) | |
275 | .collect(); | |
276 | ||
277 | let former_arg = self.p.cmd.find(name).expect(INTERNAL_ERROR_MSG); | |
278 | let usg = self.build_conflict_err_usage(matcher, conflict_ids); | |
279 | Err(Error::argument_conflict( | |
280 | self.p.cmd, former_arg, conflicts, usg, | |
281 | )) | |
282 | } | |
283 | ||
284 | fn build_conflict_err_usage(&self, matcher: &ArgMatcher, conflicting_keys: &[Id]) -> String { | |
285 | let used_filtered: Vec<Id> = matcher | |
286 | .arg_names() | |
287 | .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) | |
288 | .filter(|key| !conflicting_keys.contains(key)) | |
289 | .cloned() | |
290 | .collect(); | |
291 | let required: Vec<Id> = used_filtered | |
292 | .iter() | |
293 | .filter_map(|key| self.p.cmd.find(key)) | |
294 | .flat_map(|arg| arg.requires.iter().map(|item| &item.1)) | |
295 | .filter(|key| !used_filtered.contains(key) && !conflicting_keys.contains(key)) | |
296 | .chain(used_filtered.iter()) | |
297 | .cloned() | |
298 | .collect(); | |
299 | Usage::new(self.p.cmd) | |
300 | .required(&self.required) | |
301 | .create_usage_with_title(&required) | |
302 | } | |
303 | ||
304 | fn gather_requires(&mut self, matcher: &ArgMatcher) { | |
305 | debug!("Validator::gather_requires"); | |
306 | for name in matcher | |
307 | .arg_names() | |
308 | .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) | |
309 | { | |
310 | debug!("Validator::gather_requires:iter:{:?}", name); | |
311 | if let Some(arg) = self.p.cmd.find(name) { | |
312 | let is_relevant = |(val, req_arg): &(ArgPredicate<'_>, Id)| -> Option<Id> { | |
313 | let required = matcher.check_explicit(&arg.id, *val); | |
314 | required.then(|| req_arg.clone()) | |
315 | }; | |
316 | ||
317 | for req in self.p.cmd.unroll_arg_requires(is_relevant, &arg.id) { | |
318 | self.required.insert(req); | |
319 | } | |
320 | } else if let Some(g) = self.p.cmd.find_group(name) { | |
321 | debug!("Validator::gather_requires:iter:{:?}:group", name); | |
322 | for r in &g.requires { | |
323 | self.required.insert(r.clone()); | |
324 | } | |
325 | } | |
326 | } | |
327 | } | |
328 | ||
329 | fn validate_matched_args(&self, matcher: &ArgMatcher) -> ClapResult<()> { | |
330 | debug!("Validator::validate_matched_args"); | |
331 | matcher.iter().try_for_each(|(name, ma)| { | |
332 | debug!( | |
333 | "Validator::validate_matched_args:iter:{:?}: vals={:#?}", | |
334 | name, | |
335 | ma.vals_flatten() | |
336 | ); | |
337 | if let Some(arg) = self.p.cmd.find(name) { | |
338 | self.validate_arg_num_vals(arg, ma)?; | |
339 | self.validate_arg_values(arg, ma, matcher)?; | |
340 | self.validate_arg_num_occurs(arg, ma)?; | |
341 | } | |
342 | Ok(()) | |
343 | }) | |
344 | } | |
345 | ||
346 | fn validate_arg_num_occurs(&self, a: &Arg, ma: &MatchedArg) -> ClapResult<()> { | |
347 | debug!( | |
348 | "Validator::validate_arg_num_occurs: {:?}={}", | |
349 | a.name, | |
350 | ma.get_occurrences() | |
351 | ); | |
352 | // Occurrence of positional argument equals to number of values rather | |
353 | // than number of grouped values. | |
354 | if ma.get_occurrences() > 1 && !a.is_multiple_occurrences_set() && !a.is_positional() { | |
355 | // Not the first time, and we don't allow multiples | |
356 | return Err(Error::unexpected_multiple_usage( | |
357 | self.p.cmd, | |
358 | a, | |
359 | Usage::new(self.p.cmd) | |
360 | .required(&self.required) | |
361 | .create_usage_with_title(&[]), | |
362 | )); | |
363 | } | |
364 | if let Some(max_occurs) = a.max_occurs { | |
365 | debug!( | |
366 | "Validator::validate_arg_num_occurs: max_occurs set...{}", | |
367 | max_occurs | |
368 | ); | |
369 | let occurs = ma.get_occurrences() as usize; | |
370 | if occurs > max_occurs { | |
371 | return Err(Error::too_many_occurrences( | |
372 | self.p.cmd, | |
373 | a, | |
374 | max_occurs, | |
375 | occurs, | |
376 | Usage::new(self.p.cmd) | |
377 | .required(&self.required) | |
378 | .create_usage_with_title(&[]), | |
379 | )); | |
380 | } | |
381 | } | |
382 | ||
383 | Ok(()) | |
384 | } | |
385 | ||
386 | fn validate_arg_num_vals(&self, a: &Arg, ma: &MatchedArg) -> ClapResult<()> { | |
387 | debug!("Validator::validate_arg_num_vals"); | |
388 | if let Some(num) = a.num_vals { | |
389 | let total_num = ma.num_vals(); | |
390 | debug!("Validator::validate_arg_num_vals: num_vals set...{}", num); | |
391 | let should_err = if a.is_multiple_occurrences_set() { | |
392 | total_num % num != 0 | |
393 | } else { | |
394 | num != total_num | |
395 | }; | |
396 | if should_err { | |
397 | debug!("Validator::validate_arg_num_vals: Sending error WrongNumberOfValues"); | |
398 | return Err(Error::wrong_number_of_values( | |
399 | self.p.cmd, | |
400 | a, | |
401 | num, | |
402 | if a.is_multiple_occurrences_set() { | |
403 | total_num % num | |
404 | } else { | |
405 | total_num | |
406 | }, | |
407 | Usage::new(self.p.cmd) | |
408 | .required(&self.required) | |
409 | .create_usage_with_title(&[]), | |
410 | )); | |
411 | } | |
412 | } | |
413 | if let Some(num) = a.max_vals { | |
414 | debug!("Validator::validate_arg_num_vals: max_vals set...{}", num); | |
415 | if ma.num_vals() > num { | |
416 | debug!("Validator::validate_arg_num_vals: Sending error TooManyValues"); | |
417 | return Err(Error::too_many_values( | |
418 | self.p.cmd, | |
419 | ma.vals_flatten() | |
420 | .last() | |
421 | .expect(INTERNAL_ERROR_MSG) | |
422 | .to_str() | |
423 | .expect(INVALID_UTF8) | |
424 | .to_string(), | |
425 | a.to_string(), | |
426 | Usage::new(self.p.cmd) | |
427 | .required(&self.required) | |
428 | .create_usage_with_title(&[]), | |
429 | )); | |
430 | } | |
431 | } | |
432 | let min_vals_zero = if let Some(num) = a.min_vals { | |
433 | debug!("Validator::validate_arg_num_vals: min_vals set: {}", num); | |
434 | if ma.num_vals() < num && num != 0 { | |
435 | debug!("Validator::validate_arg_num_vals: Sending error TooFewValues"); | |
436 | return Err(Error::too_few_values( | |
437 | self.p.cmd, | |
438 | a, | |
439 | num, | |
440 | ma.num_vals(), | |
441 | Usage::new(self.p.cmd) | |
442 | .required(&self.required) | |
443 | .create_usage_with_title(&[]), | |
444 | )); | |
445 | } | |
446 | num == 0 | |
447 | } else { | |
448 | false | |
449 | }; | |
450 | // Issue 665 (https://github.com/clap-rs/clap/issues/665) | |
451 | // Issue 1105 (https://github.com/clap-rs/clap/issues/1105) | |
452 | if a.is_takes_value_set() && !min_vals_zero && ma.all_val_groups_empty() { | |
453 | return Err(Error::empty_value( | |
454 | self.p.cmd, | |
455 | &a.possible_vals | |
456 | .iter() | |
457 | .filter_map(PossibleValue::get_visible_name) | |
458 | .collect::<Vec<_>>(), | |
459 | a, | |
460 | Usage::new(self.p.cmd) | |
461 | .required(&self.required) | |
462 | .create_usage_with_title(&[]), | |
463 | )); | |
464 | } | |
465 | Ok(()) | |
466 | } | |
467 | ||
468 | fn validate_required(&mut self, matcher: &ArgMatcher) -> ClapResult<()> { | |
469 | debug!("Validator::validate_required: required={:?}", self.required); | |
470 | self.gather_requires(matcher); | |
471 | ||
472 | for arg_or_group in self.required.iter().filter(|r| !matcher.contains(r)) { | |
473 | debug!("Validator::validate_required:iter:aog={:?}", arg_or_group); | |
474 | if let Some(arg) = self.p.cmd.find(arg_or_group) { | |
475 | debug!("Validator::validate_required:iter: This is an arg"); | |
476 | if !self.is_missing_required_ok(arg, matcher) { | |
477 | return self.missing_required_error(matcher, vec![]); | |
478 | } | |
479 | } else if let Some(group) = self.p.cmd.find_group(arg_or_group) { | |
480 | debug!("Validator::validate_required:iter: This is a group"); | |
481 | if !self | |
482 | .p | |
483 | .cmd | |
484 | .unroll_args_in_group(&group.id) | |
485 | .iter() | |
486 | .any(|a| matcher.contains(a)) | |
487 | { | |
488 | return self.missing_required_error(matcher, vec![]); | |
489 | } | |
490 | } | |
491 | } | |
492 | ||
493 | // Validate the conditionally required args | |
494 | for a in self.p.cmd.get_arguments() { | |
495 | for (other, val) in &a.r_ifs { | |
496 | if matcher.check_explicit(other, ArgPredicate::Equals(std::ffi::OsStr::new(*val))) | |
497 | && !matcher.contains(&a.id) | |
498 | { | |
499 | return self.missing_required_error(matcher, vec![a.id.clone()]); | |
500 | } | |
501 | } | |
502 | ||
503 | let match_all = a.r_ifs_all.iter().all(|(other, val)| { | |
504 | matcher.check_explicit(other, ArgPredicate::Equals(std::ffi::OsStr::new(*val))) | |
505 | }); | |
506 | if match_all && !a.r_ifs_all.is_empty() && !matcher.contains(&a.id) { | |
507 | return self.missing_required_error(matcher, vec![a.id.clone()]); | |
508 | } | |
509 | } | |
510 | ||
511 | self.validate_required_unless(matcher)?; | |
512 | ||
513 | Ok(()) | |
514 | } | |
515 | ||
516 | fn is_missing_required_ok(&self, a: &Arg<'help>, matcher: &ArgMatcher) -> bool { | |
517 | debug!("Validator::is_missing_required_ok: {}", a.name); | |
518 | self.validate_arg_conflicts(a, matcher) || self.p.overridden.borrow().contains(&a.id) | |
519 | } | |
520 | ||
521 | fn validate_arg_conflicts(&self, a: &Arg<'help>, matcher: &ArgMatcher) -> bool { | |
522 | debug!("Validator::validate_arg_conflicts: a={:?}", a.name); | |
523 | a.blacklist.iter().any(|conf| { | |
524 | matcher.contains(conf) | |
525 | || self | |
526 | .p | |
527 | .cmd | |
528 | .find_group(conf) | |
529 | .map_or(false, |g| g.args.iter().any(|arg| matcher.contains(arg))) | |
530 | }) | |
531 | } | |
532 | ||
533 | fn validate_required_unless(&self, matcher: &ArgMatcher) -> ClapResult<()> { | |
534 | debug!("Validator::validate_required_unless"); | |
535 | let failed_args: Vec<_> = self | |
536 | .p | |
537 | .cmd | |
538 | .get_arguments() | |
539 | .filter(|&a| { | |
540 | (!a.r_unless.is_empty() || !a.r_unless_all.is_empty()) | |
541 | && !matcher.contains(&a.id) | |
542 | && self.fails_arg_required_unless(a, matcher) | |
543 | }) | |
544 | .map(|a| a.id.clone()) | |
545 | .collect(); | |
546 | if failed_args.is_empty() { | |
547 | Ok(()) | |
548 | } else { | |
549 | self.missing_required_error(matcher, failed_args) | |
550 | } | |
551 | } | |
552 | ||
553 | // Failing a required unless means, the arg's "unless" wasn't present, and neither were they | |
554 | fn fails_arg_required_unless(&self, a: &Arg<'help>, matcher: &ArgMatcher) -> bool { | |
555 | debug!("Validator::fails_arg_required_unless: a={:?}", a.name); | |
556 | let exists = |id| matcher.check_explicit(id, ArgPredicate::IsPresent); | |
557 | ||
558 | (a.r_unless_all.is_empty() || !a.r_unless_all.iter().all(exists)) | |
559 | && !a.r_unless.iter().any(exists) | |
560 | } | |
561 | ||
562 | // `incl`: an arg to include in the error even if not used | |
563 | fn missing_required_error(&self, matcher: &ArgMatcher, incl: Vec<Id>) -> ClapResult<()> { | |
564 | debug!("Validator::missing_required_error; incl={:?}", incl); | |
565 | debug!( | |
566 | "Validator::missing_required_error: reqs={:?}", | |
567 | self.required | |
568 | ); | |
569 | ||
570 | let usg = Usage::new(self.p.cmd).required(&self.required); | |
571 | ||
572 | let req_args = usg.get_required_usage_from(&incl, Some(matcher), true); | |
573 | ||
574 | debug!( | |
575 | "Validator::missing_required_error: req_args={:#?}", | |
576 | req_args | |
577 | ); | |
578 | ||
579 | let used: Vec<Id> = matcher | |
580 | .arg_names() | |
581 | .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) | |
582 | .filter(|n| { | |
583 | // Filter out the args we don't want to specify. | |
584 | self.p | |
585 | .cmd | |
586 | .find(n) | |
587 | .map_or(true, |a| !a.is_hide_set() && !self.required.contains(&a.id)) | |
588 | }) | |
589 | .cloned() | |
590 | .chain(incl) | |
591 | .collect(); | |
592 | ||
593 | Err(Error::missing_required_argument( | |
594 | self.p.cmd, | |
595 | req_args, | |
596 | usg.create_usage_with_title(&used), | |
597 | )) | |
598 | } | |
599 | } | |
600 | ||
601 | #[derive(Default, Clone, Debug)] | |
602 | struct Conflicts { | |
603 | potential: std::collections::HashMap<Id, Vec<Id>>, | |
604 | } | |
605 | ||
606 | impl Conflicts { | |
607 | fn new() -> Self { | |
608 | Self::default() | |
609 | } | |
610 | ||
611 | fn gather_conflicts(&mut self, cmd: &Command, matcher: &ArgMatcher, arg_id: &Id) -> Vec<Id> { | |
612 | debug!("Conflicts::gather_conflicts"); | |
613 | let mut conflicts = Vec::new(); | |
614 | for other_arg_id in matcher | |
615 | .arg_names() | |
616 | .filter(|arg_id| matcher.check_explicit(arg_id, ArgPredicate::IsPresent)) | |
617 | { | |
618 | if arg_id == other_arg_id { | |
619 | continue; | |
620 | } | |
621 | ||
622 | if self | |
623 | .gather_direct_conflicts(cmd, arg_id) | |
624 | .contains(other_arg_id) | |
625 | { | |
626 | conflicts.push(other_arg_id.clone()); | |
627 | } | |
628 | if self | |
629 | .gather_direct_conflicts(cmd, other_arg_id) | |
630 | .contains(arg_id) | |
631 | { | |
632 | conflicts.push(other_arg_id.clone()); | |
633 | } | |
634 | } | |
635 | conflicts | |
636 | } | |
637 | ||
638 | fn gather_direct_conflicts(&mut self, cmd: &Command, arg_id: &Id) -> &[Id] { | |
639 | self.potential.entry(arg_id.clone()).or_insert_with(|| { | |
640 | let conf = if let Some(arg) = cmd.find(arg_id) { | |
641 | let mut conf = arg.blacklist.clone(); | |
642 | for group_id in cmd.groups_for_arg(arg_id) { | |
643 | let group = cmd.find_group(&group_id).expect(INTERNAL_ERROR_MSG); | |
644 | conf.extend(group.conflicts.iter().cloned()); | |
645 | if !group.multiple { | |
646 | for member_id in &group.args { | |
647 | if member_id != arg_id { | |
648 | conf.push(member_id.clone()); | |
649 | } | |
650 | } | |
651 | } | |
652 | } | |
653 | conf | |
654 | } else if let Some(group) = cmd.find_group(arg_id) { | |
655 | group.conflicts.clone() | |
656 | } else { | |
657 | debug_assert!(false, "id={:?} is unknown", arg_id); | |
658 | Vec::new() | |
659 | }; | |
660 | debug!( | |
661 | "Conflicts::gather_direct_conflicts id={:?}, conflicts={:?}", | |
662 | arg_id, conf | |
663 | ); | |
664 | conf | |
665 | }) | |
666 | } | |
667 | } |