1use std::cmp::Ordering;
2
3use crate::builder::ValueRange;
4use crate::mkeymap::KeyType;
5use crate::util::FlatSet;
6use crate::util::Id;
7use crate::ArgAction;
8use crate::INTERNAL_ERROR_MSG;
9use crate::{Arg, Command, ValueHint};
10
11pub(crate) fn assert_app(cmd: &Command) {
12 debug!("Command::_debug_asserts");
13
14 let mut short_flags = vec![];
15 let mut long_flags = vec![];
16
17 // Invalid version flag settings
18 if cmd.get_version().is_none() && cmd.get_long_version().is_none() {
19 // PropagateVersion is meaningless if there is no version
20 assert!(
21 !cmd.is_propagate_version_set(),
22 "Command {}: No version information via Command::version or Command::long_version to propagate",
23 cmd.get_name(),
24 );
25
26 // Used `Command::mut_arg("version", ..) but did not provide any version information to display
27 let version_needed = cmd
28 .get_arguments()
29 .filter(|x| matches!(x.get_action(), ArgAction::Version))
30 .map(|x| x.get_id())
31 .collect::<Vec<_>>();
32
33 assert_eq!(version_needed, Vec::<&str>::new(), "Command {}: `ArgAction::Version` used without providing Command::version or Command::long_version"
34 ,cmd.get_name()
35 );
36 }
37
38 for sc in cmd.get_subcommands() {
39 if let Some(s) = sc.get_short_flag().as_ref() {
40 short_flags.push(Flag::Command(format!("-{s}"), sc.get_name()));
41 }
42
43 for short_alias in sc.get_all_short_flag_aliases() {
44 short_flags.push(Flag::Command(format!("-{short_alias}"), sc.get_name()));
45 }
46
47 if let Some(l) = sc.get_long_flag().as_ref() {
48 assert!(!l.starts_with('-'), "Command {}: long_flag {:?} must not start with a `-`, that will be handled by the parser", sc.get_name(), l);
49 long_flags.push(Flag::Command(format!("--{l}"), sc.get_name()));
50 }
51
52 for long_alias in sc.get_all_long_flag_aliases() {
53 long_flags.push(Flag::Command(format!("--{long_alias}"), sc.get_name()));
54 }
55 }
56
57 for arg in cmd.get_arguments() {
58 assert_arg(arg);
59
60 assert!(
61 !cmd.is_multicall_set(),
62 "Command {}: Arguments like {} cannot be set on a multicall command",
63 cmd.get_name(),
64 arg.get_id()
65 );
66
67 if let Some(s) = arg.get_short() {
68 short_flags.push(Flag::Arg(format!("-{s}"), arg.get_id().as_str()));
69 }
70
71 for (short_alias, _) in &arg.short_aliases {
72 short_flags.push(Flag::Arg(format!("-{short_alias}"), arg.get_id().as_str()));
73 }
74
75 if let Some(l) = arg.get_long() {
76 assert!(!l.starts_with('-'), "Argument {}: long {:?} must not start with a `-`, that will be handled by the parser", arg.get_id(), l);
77 long_flags.push(Flag::Arg(format!("--{l}"), arg.get_id().as_str()));
78 }
79
80 for (long_alias, _) in &arg.aliases {
81 long_flags.push(Flag::Arg(format!("--{long_alias}"), arg.get_id().as_str()));
82 }
83
84 // Name conflicts
85 if let Some((first, second)) = cmd.two_args_of(|x| x.get_id() == arg.get_id()) {
86 panic!(
87 "Command {}: Argument names must be unique, but '{}' is in use by more than one argument or group{}",
88 cmd.get_name(),
89 arg.get_id(),
90 duplicate_tip(cmd, first, second),
91 );
92 }
93
94 // Long conflicts
95 if let Some(l) = arg.get_long() {
96 if let Some((first, second)) = cmd.two_args_of(|x| x.get_long() == Some(l)) {
97 panic!(
98 "Command {}: Long option names must be unique for each argument, \
99 but '--{}' is in use by both '{}' and '{}'{}",
100 cmd.get_name(),
101 l,
102 first.get_id(),
103 second.get_id(),
104 duplicate_tip(cmd, first, second)
105 )
106 }
107 }
108
109 // Short conflicts
110 if let Some(s) = arg.get_short() {
111 if let Some((first, second)) = cmd.two_args_of(|x| x.get_short() == Some(s)) {
112 panic!(
113 "Command {}: Short option names must be unique for each argument, \
114 but '-{}' is in use by both '{}' and '{}'{}",
115 cmd.get_name(),
116 s,
117 first.get_id(),
118 second.get_id(),
119 duplicate_tip(cmd, first, second),
120 )
121 }
122 }
123
124 // Index conflicts
125 if let Some(idx) = arg.index {
126 if let Some((first, second)) =
127 cmd.two_args_of(|x| x.is_positional() && x.get_index() == Some(idx))
128 {
129 panic!(
130 "Command {}: Argument '{}' has the same index as '{}' \
131 and they are both positional arguments\n\n\t \
132 Use `Arg::num_args(1..)` to allow one \
133 positional argument to take multiple values",
134 cmd.get_name(),
135 first.get_id(),
136 second.get_id()
137 )
138 }
139 }
140
141 // requires, r_if, r_unless
142 for req in &arg.requires {
143 assert!(
144 cmd.id_exists(&req.1),
145 "Command {}: Argument or group '{}' specified in 'requires*' for '{}' does not exist",
146 cmd.get_name(),
147 req.1,
148 arg.get_id(),
149 );
150 }
151
152 for req in &arg.r_ifs {
153 assert!(
154 !arg.is_required_set(),
155 "Argument {}: `required` conflicts with `required_if_eq*`",
156 arg.get_id()
157 );
158 assert!(
159 cmd.id_exists(&req.0),
160 "Command {}: Argument or group '{}' specified in 'required_if_eq*' for '{}' does not exist",
161 cmd.get_name(),
162 req.0,
163 arg.get_id()
164 );
165 }
166
167 for req in &arg.r_ifs_all {
168 assert!(
169 !arg.is_required_set(),
170 "Argument {}: `required` conflicts with `required_if_eq_all`",
171 arg.get_id()
172 );
173 assert!(
174 cmd.id_exists(&req.0),
175 "Command {}: Argument or group '{}' specified in 'required_if_eq_all' for '{}' does not exist",
176 cmd.get_name(),
177 req.0,
178 arg.get_id()
179 );
180 }
181
182 for req in &arg.r_unless {
183 assert!(
184 !arg.is_required_set(),
185 "Argument {}: `required` conflicts with `required_unless*`",
186 arg.get_id()
187 );
188 assert!(
189 cmd.id_exists(req),
190 "Command {}: Argument or group '{}' specified in 'required_unless*' for '{}' does not exist",
191 cmd.get_name(),
192 req,
193 arg.get_id(),
194 );
195 }
196
197 for req in &arg.r_unless_all {
198 assert!(
199 !arg.is_required_set(),
200 "Argument {}: `required` conflicts with `required_unless*`",
201 arg.get_id()
202 );
203 assert!(
204 cmd.id_exists(req),
205 "Command {}: Argument or group '{}' specified in 'required_unless*' for '{}' does not exist",
206 cmd.get_name(),
207 req,
208 arg.get_id(),
209 );
210 }
211
212 // blacklist
213 for req in &arg.blacklist {
214 assert!(
215 cmd.id_exists(req),
216 "Command {}: Argument or group '{}' specified in 'conflicts_with*' for '{}' does not exist",
217 cmd.get_name(),
218 req,
219 arg.get_id(),
220 );
221 }
222
223 // overrides
224 for req in &arg.overrides {
225 assert!(
226 cmd.id_exists(req),
227 "Command {}: Argument or group '{}' specified in 'overrides_with*' for '{}' does not exist",
228 cmd.get_name(),
229 req,
230 arg.get_id(),
231 );
232 }
233
234 if arg.is_last_set() {
235 assert!(
236 arg.get_long().is_none(),
237 "Command {}: Flags or Options cannot have last(true) set. '{}' has both a long and last(true) set.",
238 cmd.get_name(),
239 arg.get_id()
240 );
241 assert!(
242 arg.get_short().is_none(),
243 "Command {}: Flags or Options cannot have last(true) set. '{}' has both a short and last(true) set.",
244 cmd.get_name(),
245 arg.get_id()
246 );
247 }
248
249 assert!(
250 !(arg.is_required_set() && arg.is_global_set()),
251 "Command {}: Global arguments cannot be required.\n\n\t'{}' is marked as both global and required",
252 cmd.get_name(),
253 arg.get_id()
254 );
255
256 if arg.get_value_hint() == ValueHint::CommandWithArguments {
257 assert!(
258 arg.is_positional(),
259 "Command {}: Argument '{}' has hint CommandWithArguments and must be positional.",
260 cmd.get_name(),
261 arg.get_id()
262 );
263
264 assert!(
265 arg.is_trailing_var_arg_set() || arg.is_last_set(),
266 "Command {}: Positional argument '{}' has hint CommandWithArguments, so Command must have `trailing_var_arg(true)` or `last(true)` set.",
267 cmd.get_name(),
268 arg.get_id()
269 );
270 }
271 }
272
273 for group in cmd.get_groups() {
274 // Name conflicts
275 assert!(
276 cmd.get_groups().filter(|x| x.id == group.id).count() < 2,
277 "Command {}: Argument group name must be unique\n\n\t'{}' is already in use",
278 cmd.get_name(),
279 group.get_id(),
280 );
281
282 // Groups should not have naming conflicts with Args
283 assert!(
284 !cmd.get_arguments().any(|x| x.get_id() == group.get_id()),
285 "Command {}: Argument group name '{}' must not conflict with argument name",
286 cmd.get_name(),
287 group.get_id(),
288 );
289
290 for arg in &group.args {
291 // Args listed inside groups should exist
292 assert!(
293 cmd.get_arguments().any(|x| x.get_id() == arg),
294 "Command {}: Argument group '{}' contains non-existent argument '{}'",
295 cmd.get_name(),
296 group.get_id(),
297 arg
298 );
299 }
300
301 for arg in &group.requires {
302 // Args listed inside groups should exist
303 assert!(
304 cmd.id_exists(arg),
305 "Command {}: Argument group '{}' requires non-existent '{}' id",
306 cmd.get_name(),
307 group.get_id(),
308 arg
309 );
310 }
311
312 for arg in &group.conflicts {
313 // Args listed inside groups should exist
314 assert!(
315 cmd.id_exists(arg),
316 "Command {}: Argument group '{}' conflicts with non-existent '{}' id",
317 cmd.get_name(),
318 group.get_id(),
319 arg
320 );
321 }
322 }
323
324 // Conflicts between flags and subcommands
325
326 long_flags.sort_unstable();
327 short_flags.sort_unstable();
328
329 detect_duplicate_flags(&long_flags, "long");
330 detect_duplicate_flags(&short_flags, "short");
331
332 let mut subs = FlatSet::new();
333 for sc in cmd.get_subcommands() {
334 assert!(
335 subs.insert(sc.get_name()),
336 "Command {}: command name `{}` is duplicated",
337 cmd.get_name(),
338 sc.get_name()
339 );
340 for alias in sc.get_all_aliases() {
341 assert!(
342 subs.insert(alias),
343 "Command {}: command `{}` alias `{}` is duplicated",
344 cmd.get_name(),
345 sc.get_name(),
346 alias
347 );
348 }
349 }
350
351 _verify_positionals(cmd);
352
353 #[cfg(feature = "help")]
354 if let Some(help_template) = cmd.get_help_template() {
355 assert!(
356 !help_template.to_string().contains("{flags}"),
357 "Command {}: {}",
358 cmd.get_name(),
359 "`{flags}` template variable was removed in clap3, they are now included in `{options}`",
360 );
361 assert!(
362 !help_template.to_string().contains("{unified}"),
363 "Command {}: {}",
364 cmd.get_name(),
365 "`{unified}` template variable was removed in clap3, use `{options}` instead"
366 );
367 #[cfg(feature = "unstable-v5")]
368 assert!(
369 !help_template.to_string().contains("{bin}"),
370 "Command {}: {}",
371 cmd.get_name(),
372 "`{bin}` template variable was removed in clap5, use `{name}` instead"
373 )
374 }
375
376 cmd._panic_on_missing_help(cmd.is_help_expected_set());
377 assert_app_flags(cmd);
378}
379
380fn duplicate_tip(cmd: &Command, first: &Arg, second: &Arg) -> &'static str {
381 if !cmd.is_disable_help_flag_set()
382 && (first.get_id() == Id::HELP || second.get_id() == Id::HELP)
383 {
384 " (call `cmd.disable_help_flag(true)` to remove the auto-generated `--help`)"
385 } else if !cmd.is_disable_version_flag_set()
386 && (first.get_id() == Id::VERSION || second.get_id() == Id::VERSION)
387 {
388 " (call `cmd.disable_version_flag(true)` to remove the auto-generated `--version`)"
389 } else {
390 ""
391 }
392}
393
394#[derive(Eq)]
395enum Flag<'a> {
396 Command(String, &'a str),
397 Arg(String, &'a str),
398}
399
400impl PartialEq for Flag<'_> {
401 fn eq(&self, other: &Flag) -> bool {
402 self.cmp(other) == Ordering::Equal
403 }
404}
405
406impl PartialOrd for Flag<'_> {
407 fn partial_cmp(&self, other: &Flag) -> Option<Ordering> {
408 use Flag::*;
409
410 match (self, other) {
411 (Command(s1: &String, _), Command(s2: &String, _))
412 | (Arg(s1: &String, _), Arg(s2: &String, _))
413 | (Command(s1: &String, _), Arg(s2: &String, _))
414 | (Arg(s1: &String, _), Command(s2: &String, _)) => {
415 if s1 == s2 {
416 Some(Ordering::Equal)
417 } else {
418 s1.partial_cmp(s2)
419 }
420 }
421 }
422 }
423}
424
425impl Ord for Flag<'_> {
426 fn cmp(&self, other: &Self) -> Ordering {
427 self.partial_cmp(other).unwrap()
428 }
429}
430
431fn detect_duplicate_flags(flags: &[Flag], short_or_long: &str) {
432 use Flag::*;
433
434 for (one: &Flag<'_>, two: &Flag<'_>) in find_duplicates(slice:flags) {
435 match (one, two) {
436 (Command(flag: &String, one: &&str), Command(_, another: &&str)) if one != another => panic!(
437 "the '{flag}' {short_or_long} flag is specified for both '{one}' and '{another}' subcommands"
438 ),
439
440 (Arg(flag: &String, one: &&str), Arg(_, another: &&str)) if one != another => panic!(
441 "{short_or_long} option names must be unique, but '{flag}' is in use by both '{one}' and '{another}'"
442 ),
443
444 (Arg(flag: &String, arg: &&str), Command(_, sub: &&str)) | (Command(flag: &String, sub: &&str), Arg(_, arg: &&str)) => panic!(
445 "the '{flag}' {short_or_long} flag for the '{arg}' argument conflicts with the short flag \
446 for '{sub}' subcommand"
447 ),
448
449 _ => {}
450 }
451 }
452}
453
454/// Find duplicates in a sorted array.
455///
456/// The algorithm is simple: the array is sorted, duplicates
457/// must be placed next to each other, we can check only adjacent elements.
458fn find_duplicates<T: PartialEq>(slice: &[T]) -> impl Iterator<Item = (&T, &T)> {
459 slice.windows(size:2).filter_map(|w: &[T]| {
460 if w[0] == w[1] {
461 Some((&w[0], &w[1]))
462 } else {
463 None
464 }
465 })
466}
467
468fn assert_app_flags(cmd: &Command) {
469 macro_rules! checker {
470 ($a:ident requires $($b:ident)|+) => {
471 if cmd.$a() {
472 let mut s = String::new();
473
474 $(
475 if !cmd.$b() {
476 use std::fmt::Write;
477 write!(&mut s, " AppSettings::{} is required when AppSettings::{} is set.\n", std::stringify!($b), std::stringify!($a)).unwrap();
478 }
479 )+
480
481 if !s.is_empty() {
482 panic!("{s}")
483 }
484 }
485 };
486 ($a:ident conflicts $($b:ident)|+) => {
487 if cmd.$a() {
488 let mut s = String::new();
489
490 $(
491 if cmd.$b() {
492 use std::fmt::Write;
493 write!(&mut s, " AppSettings::{} conflicts with AppSettings::{}.\n", std::stringify!($b), std::stringify!($a)).unwrap();
494 }
495 )+
496
497 if !s.is_empty() {
498 panic!("{}\n{}", cmd.get_name(), s)
499 }
500 }
501 };
502 }
503
504 checker!(is_multicall_set conflicts is_no_binary_name_set);
505}
506
507#[cfg(debug_assertions)]
508fn _verify_positionals(cmd: &Command) -> bool {
509 debug!("Command::_verify_positionals");
510 // Because you must wait until all arguments have been supplied, this is the first chance
511 // to make assertions on positional argument indexes
512 //
513 // First we verify that the index highest supplied index, is equal to the number of
514 // positional arguments to verify there are no gaps (i.e. supplying an index of 1 and 3
515 // but no 2)
516
517 let highest_idx = cmd
518 .get_keymap()
519 .keys()
520 .filter_map(|x| {
521 if let KeyType::Position(n) = x {
522 Some(*n)
523 } else {
524 None
525 }
526 })
527 .max()
528 .unwrap_or(0);
529
530 let num_p = cmd.get_keymap().keys().filter(|x| x.is_position()).count();
531
532 assert!(
533 highest_idx == num_p,
534 "Found positional argument whose index is {highest_idx} but there \
535 are only {num_p} positional arguments defined",
536 );
537
538 for arg in cmd.get_arguments() {
539 if arg.index.unwrap_or(0) == highest_idx {
540 assert!(
541 !arg.is_trailing_var_arg_set() || !arg.is_last_set(),
542 "{}:{}: `Arg::trailing_var_arg` and `Arg::last` cannot be used together",
543 cmd.get_name(),
544 arg.get_id()
545 );
546
547 if arg.is_trailing_var_arg_set() {
548 assert!(
549 arg.is_multiple(),
550 "{}:{}: `Arg::trailing_var_arg` must accept multiple values",
551 cmd.get_name(),
552 arg.get_id()
553 );
554 }
555 } else {
556 assert!(
557 !arg.is_trailing_var_arg_set(),
558 "{}:{}: `Arg::trailing_var_arg` can only apply to last positional",
559 cmd.get_name(),
560 arg.get_id()
561 );
562 }
563 }
564
565 // Next we verify that only the highest index has takes multiple arguments (if any)
566 let only_highest = |a: &Arg| a.is_multiple() && (a.get_index().unwrap_or(0) != highest_idx);
567 if cmd.get_positionals().any(only_highest) {
568 // First we make sure if there is a positional that allows multiple values
569 // the one before it (second to last) has one of these:
570 // * a value terminator
571 // * ArgSettings::Last
572 // * The last arg is Required
573
574 // We can't pass the closure (it.next()) to the macro directly because each call to
575 // find() (iterator, not macro) gets called repeatedly.
576 let last = &cmd.get_keymap()[&KeyType::Position(highest_idx)];
577 let second_to_last = &cmd.get_keymap()[&KeyType::Position(highest_idx - 1)];
578
579 // Either the final positional is required
580 // Or the second to last has a terminator or .last(true) set
581 let ok = last.is_required_set()
582 || (second_to_last.terminator.is_some() || second_to_last.is_last_set())
583 || last.is_last_set();
584 assert!(
585 ok,
586 "Positional argument `{last}` *must* have `required(true)` or `last(true)` set \
587 because a prior positional argument (`{second_to_last}`) has `num_args(1..)`"
588 );
589
590 // We make sure if the second to last is Multiple the last is ArgSettings::Last
591 let ok = second_to_last.is_multiple() || last.is_last_set();
592 assert!(
593 ok,
594 "Only the last positional argument, or second to last positional \
595 argument may be set to `.num_args(1..)`"
596 );
597
598 // Next we check how many have both Multiple and not a specific number of values set
599 let count = cmd
600 .get_positionals()
601 .filter(|p| {
602 p.is_multiple_values_set()
603 && p.get_value_terminator().is_none()
604 && !p.get_num_args().expect(INTERNAL_ERROR_MSG).is_fixed()
605 })
606 .count();
607 let ok = count <= 1
608 || (last.is_last_set()
609 && last.is_multiple()
610 && second_to_last.is_multiple()
611 && count == 2);
612 assert!(
613 ok,
614 "Only one positional argument with `.num_args(1..)` set is allowed per \
615 command, unless the second one also has .last(true) set"
616 );
617 }
618
619 let mut found = false;
620
621 if cmd.is_allow_missing_positional_set() {
622 // Check that if a required positional argument is found, all positions with a lower
623 // index are also required.
624 let mut foundx2 = false;
625
626 for p in cmd.get_positionals() {
627 if foundx2 && !p.is_required_set() {
628 assert!(
629 p.is_required_set(),
630 "Found non-required positional argument with a lower \
631 index than a required positional argument by two or more: {:?} \
632 index {:?}",
633 p.get_id(),
634 p.get_index()
635 );
636 } else if p.is_required_set() && !p.is_last_set() {
637 // Args that .last(true) don't count since they can be required and have
638 // positionals with a lower index that aren't required
639 // Imagine: prog <req1> [opt1] -- <req2>
640 // Both of these are valid invocations:
641 // $ prog r1 -- r2
642 // $ prog r1 o1 -- r2
643 if found {
644 foundx2 = true;
645 continue;
646 }
647 found = true;
648 continue;
649 } else {
650 found = false;
651 }
652 }
653 } else {
654 // Check that if a required positional argument is found, all positions with a lower
655 // index are also required
656 for p in (1..=num_p).rev().filter_map(|n| cmd.get_keymap().get(&n)) {
657 if found {
658 assert!(
659 p.is_required_set(),
660 "Found non-required positional argument with a lower \
661 index than a required positional argument: {:?} index {:?}",
662 p.get_id(),
663 p.get_index()
664 );
665 } else if p.is_required_set() && !p.is_last_set() {
666 // Args that .last(true) don't count since they can be required and have
667 // positionals with a lower index that aren't required
668 // Imagine: prog <req1> [opt1] -- <req2>
669 // Both of these are valid invocations:
670 // $ prog r1 -- r2
671 // $ prog r1 o1 -- r2
672 found = true;
673 continue;
674 }
675 }
676 }
677 assert!(
678 cmd.get_positionals().filter(|p| p.is_last_set()).count() < 2,
679 "Only one positional argument may have last(true) set. Found two."
680 );
681 if cmd
682 .get_positionals()
683 .any(|p| p.is_last_set() && p.is_required_set())
684 && cmd.has_subcommands()
685 && !cmd.is_subcommand_negates_reqs_set()
686 {
687 panic!(
688 "Having a required positional argument with .last(true) set *and* child \
689 subcommands without setting SubcommandsNegateReqs isn't compatible."
690 );
691 }
692
693 true
694}
695
696fn assert_arg(arg: &Arg) {
697 debug!("Arg::_debug_asserts:{}", arg.get_id());
698
699 // Self conflict
700 // TODO: this check should be recursive
701 assert!(
702 !arg.blacklist.iter().any(|x| x == arg.get_id()),
703 "Argument '{}' cannot conflict with itself",
704 arg.get_id(),
705 );
706
707 assert_eq!(
708 arg.get_action().takes_values(),
709 arg.is_takes_value_set(),
710 "Argument `{}`'s selected action {:?} contradicts `takes_value`",
711 arg.get_id(),
712 arg.get_action()
713 );
714 if let Some(action_type_id) = arg.get_action().value_type_id() {
715 assert_eq!(
716 action_type_id,
717 arg.get_value_parser().type_id(),
718 "Argument `{}`'s selected action {:?} contradicts `value_parser` ({:?})",
719 arg.get_id(),
720 arg.get_action(),
721 arg.get_value_parser()
722 );
723 }
724
725 if arg.get_value_hint() != ValueHint::Unknown {
726 assert!(
727 arg.is_takes_value_set(),
728 "Argument '{}' has value hint but takes no value",
729 arg.get_id()
730 );
731
732 if arg.get_value_hint() == ValueHint::CommandWithArguments {
733 assert!(
734 arg.is_multiple_values_set(),
735 "Argument '{}' uses hint CommandWithArguments and must accept multiple values",
736 arg.get_id()
737 )
738 }
739 }
740
741 if arg.index.is_some() {
742 assert!(
743 arg.is_positional(),
744 "Argument '{}' is a positional argument and can't have short or long name versions",
745 arg.get_id()
746 );
747 assert!(
748 arg.is_takes_value_set(),
749 "Argument '{}` is positional, it must take a value{}",
750 arg.get_id(),
751 if arg.get_id() == Id::HELP {
752 " (`mut_arg` no longer works with implicit `--help`)"
753 } else if arg.get_id() == Id::VERSION {
754 " (`mut_arg` no longer works with implicit `--version`)"
755 } else {
756 ""
757 }
758 );
759 }
760
761 let num_vals = arg.get_num_args().expect(INTERNAL_ERROR_MSG);
762 // This can be the cause of later asserts, so put this first
763 if num_vals != ValueRange::EMPTY {
764 // HACK: Don't check for flags to make the derive easier
765 let num_val_names = arg.get_value_names().unwrap_or(&[]).len();
766 if num_vals.max_values() < num_val_names {
767 panic!(
768 "Argument {}: Too many value names ({}) compared to `num_args` ({})",
769 arg.get_id(),
770 num_val_names,
771 num_vals
772 );
773 }
774 }
775
776 assert_eq!(
777 num_vals.takes_values(),
778 arg.is_takes_value_set(),
779 "Argument {}: mismatch between `num_args` ({}) and `takes_value`",
780 arg.get_id(),
781 num_vals,
782 );
783 assert_eq!(
784 num_vals.is_multiple(),
785 arg.is_multiple_values_set(),
786 "Argument {}: mismatch between `num_args` ({}) and `multiple_values`",
787 arg.get_id(),
788 num_vals,
789 );
790
791 if 1 < num_vals.min_values() {
792 assert!(
793 !arg.is_require_equals_set(),
794 "Argument {}: cannot accept more than 1 arg (num_args={}) with require_equals",
795 arg.get_id(),
796 num_vals
797 );
798 }
799
800 if num_vals == ValueRange::SINGLE {
801 assert!(
802 !arg.is_multiple_values_set(),
803 "Argument {}: mismatch between `num_args` and `multiple_values`",
804 arg.get_id()
805 );
806 }
807
808 assert_arg_flags(arg);
809}
810
811fn assert_arg_flags(arg: &Arg) {
812 macro_rules! checker {
813 ($a:ident requires $($b:ident)|+) => {
814 if arg.$a() {
815 let mut s = String::new();
816
817 $(
818 if !arg.$b() {
819 use std::fmt::Write;
820 write!(&mut s, " Arg::{} is required when Arg::{} is set.\n", std::stringify!($b), std::stringify!($a)).unwrap();
821 }
822 )+
823
824 if !s.is_empty() {
825 panic!("Argument {:?}\n{}", arg.get_id(), s)
826 }
827 }
828 }
829 }
830
831 checker!(is_hide_possible_values_set requires is_takes_value_set);
832 checker!(is_allow_hyphen_values_set requires is_takes_value_set);
833 checker!(is_allow_negative_numbers_set requires is_takes_value_set);
834 checker!(is_require_equals_set requires is_takes_value_set);
835 checker!(is_last_set requires is_takes_value_set);
836 checker!(is_hide_default_value_set requires is_takes_value_set);
837 checker!(is_multiple_values_set requires is_takes_value_set);
838 checker!(is_ignore_case_set requires is_takes_value_set);
839}
840