1use std::cmp::Ordering;
23use crate::ArgAction;
4use crate::INTERNAL_ERROR_MSG;
5use crate::builder::ValueRange;
6use crate::mkeymap::KeyType;
7use crate::util::FlatSet;
8use crate::util::Id;
9use crate::{Arg, Command, ValueHint};
1011pub(crate) fn assert_app(cmd: &Command) {
12debug!("Command::_debug_asserts");
1314let mut short_flags = ::alloc::vec::Vec::new()vec![];
15let mut long_flags = ::alloc::vec::Vec::new()vec![];
1617// Invalid version flag settings
18if cmd.get_version().is_none() && cmd.get_long_version().is_none() {
19// PropagateVersion is meaningless if there is no version
20if !!cmd.is_propagate_version_set() {
{
::core::panicking::panic_fmt(format_args!("Command {0}: No version information via Command::version or Command::long_version to propagate",
cmd.get_name()));
}
};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 );
2526// Used `Command::mut_arg("version", ..) but did not provide any version information to display
27let version_needed = cmd28 .get_arguments()
29 .filter(|x| #[allow(non_exhaustive_omitted_patterns)] match x.get_action() {
ArgAction::Version => true,
_ => false,
}matches!(x.get_action(), ArgAction::Version))
30 .map(|x| x.get_id())
31 .collect::<Vec<_>>();
3233match (&version_needed, &Vec::<&str>::new()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::Some(format_args!("Command {0}: `ArgAction::Version` used without providing Command::version or Command::long_version",
cmd.get_name())));
}
}
};assert_eq!(
34 version_needed,
35 Vec::<&str>::new(),
36"Command {}: `ArgAction::Version` used without providing Command::version or Command::long_version",
37 cmd.get_name()
38 );
39 }
4041for sc in cmd.get_subcommands() {
42if let Some(s) = sc.get_short_flag().as_ref() {
43 short_flags.push(Flag::Command(::alloc::__export::must_use({ ::alloc::fmt::format(format_args!("-{0}", s)) })format!("-{s}"), sc.get_name()));
44 }
4546for short_alias in sc.get_all_short_flag_aliases() {
47 short_flags.push(Flag::Command(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("-{0}", short_alias))
})format!("-{short_alias}"), sc.get_name()));
48 }
4950if let Some(l) = sc.get_long_flag().as_ref() {
51if !!l.starts_with('-') {
{
::core::panicking::panic_fmt(format_args!("Command {0}: long_flag {1:?} must not start with a `-`, that will be handled by the parser",
sc.get_name(), l));
}
};assert!(
52 !l.starts_with('-'),
53"Command {}: long_flag {:?} must not start with a `-`, that will be handled by the parser",
54 sc.get_name(),
55 l
56 );
57 long_flags.push(Flag::Command(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("--{0}", l))
})format!("--{l}"), sc.get_name()));
58 }
5960for long_alias in sc.get_all_long_flag_aliases() {
61 long_flags.push(Flag::Command(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("--{0}", long_alias))
})format!("--{long_alias}"), sc.get_name()));
62 }
63 }
6465for arg in cmd.get_arguments() {
66 assert_arg(arg);
6768if !!cmd.is_multicall_set() {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Arguments like {1} cannot be set on a multicall command",
cmd.get_name(), arg.get_id()));
}
};assert!(
69 !cmd.is_multicall_set(),
70"Command {}: Arguments like {} cannot be set on a multicall command",
71 cmd.get_name(),
72 arg.get_id()
73 );
7475if let Some(s) = arg.get_short() {
76 short_flags.push(Flag::Arg(::alloc::__export::must_use({ ::alloc::fmt::format(format_args!("-{0}", s)) })format!("-{s}"), arg.get_id().as_str()));
77 }
7879for (short_alias, _) in &arg.short_aliases {
80 short_flags.push(Flag::Arg(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("-{0}", short_alias))
})format!("-{short_alias}"), arg.get_id().as_str()));
81 }
8283if let Some(l) = arg.get_long() {
84if !!l.starts_with('-') {
{
::core::panicking::panic_fmt(format_args!("Argument {0}: long {1:?} must not start with a `-`, that will be handled by the parser",
arg.get_id(), l));
}
};assert!(
85 !l.starts_with('-'),
86"Argument {}: long {:?} must not start with a `-`, that will be handled by the parser",
87 arg.get_id(),
88 l
89 );
90 long_flags.push(Flag::Arg(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("--{0}", l))
})format!("--{l}"), arg.get_id().as_str()));
91 }
9293for (long_alias, _) in &arg.aliases {
94 long_flags.push(Flag::Arg(::alloc::__export::must_use({
::alloc::fmt::format(format_args!("--{0}", long_alias))
})format!("--{long_alias}"), arg.get_id().as_str()));
95 }
9697// Name conflicts
98if let Some((first, second)) = cmd.two_args_of(|x| x.get_id() == arg.get_id()) {
99{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument names must be unique, but \'{1}\' is in use by more than one argument or group{2}",
cmd.get_name(), arg.get_id(), duplicate_tip(cmd, first, second)));
};panic!(
100"Command {}: Argument names must be unique, but '{}' is in use by more than one argument or group{}",
101 cmd.get_name(),
102 arg.get_id(),
103 duplicate_tip(cmd, first, second),
104 );
105 }
106107// Long conflicts
108if let Some(l) = arg.get_long() {
109if let Some((first, second)) = cmd.two_args_of(|x| x.get_long() == Some(l)) {
110{
::core::panicking::panic_fmt(format_args!("Command {0}: Long option names must be unique for each argument, but \'--{1}\' is in use by both \'{2}\' and \'{3}\'{4}",
cmd.get_name(), l, first.get_id(), second.get_id(),
duplicate_tip(cmd, first, second)));
}panic!(
111"Command {}: Long option names must be unique for each argument, \
112 but '--{}' is in use by both '{}' and '{}'{}",
113 cmd.get_name(),
114 l,
115 first.get_id(),
116 second.get_id(),
117 duplicate_tip(cmd, first, second)
118 )119 }
120 }
121122// Short conflicts
123if let Some(s) = arg.get_short() {
124if let Some((first, second)) = cmd.two_args_of(|x| x.get_short() == Some(s)) {
125{
::core::panicking::panic_fmt(format_args!("Command {0}: Short option names must be unique for each argument, but \'-{1}\' is in use by both \'{2}\' and \'{3}\'{4}",
cmd.get_name(), s, first.get_id(), second.get_id(),
duplicate_tip(cmd, first, second)));
}panic!(
126"Command {}: Short option names must be unique for each argument, \
127 but '-{}' is in use by both '{}' and '{}'{}",
128 cmd.get_name(),
129 s,
130 first.get_id(),
131 second.get_id(),
132 duplicate_tip(cmd, first, second),
133 )134 }
135 }
136137// Index conflicts
138if let Some(idx) = arg.index {
139if let Some((first, second)) =
140 cmd.two_args_of(|x| x.is_positional() && x.get_index() == Some(idx))
141 {
142{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument \'{1}\' has the same index as \'{2}\' and they are both positional arguments\n\n\t Use `Arg::num_args(1..)` to allow one positional argument to take multiple values",
cmd.get_name(), first.get_id(), second.get_id()));
}panic!(
143"Command {}: Argument '{}' has the same index as '{}' \
144 and they are both positional arguments\n\n\t \
145 Use `Arg::num_args(1..)` to allow one \
146 positional argument to take multiple values",
147 cmd.get_name(),
148 first.get_id(),
149 second.get_id()
150 )151 }
152 }
153154// requires, r_if, r_unless
155for (_predicate, req_id) in &arg.requires {
156if !(&arg.id != req_id) {
{
::core::panicking::panic_fmt(format_args!("Argument {0} cannot require itself",
arg.get_id()));
}
};assert!(
157&arg.id != req_id,
158"Argument {} cannot require itself",
159 arg.get_id()
160 );
161162if !cmd.id_exists(req_id) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument or group \'{1}\' specified in \'requires*\' for \'{2}\' does not exist",
cmd.get_name(), req_id, arg.get_id()));
}
};assert!(
163 cmd.id_exists(req_id),
164"Command {}: Argument or group '{}' specified in 'requires*' for '{}' does not exist",
165 cmd.get_name(),
166 req_id,
167 arg.get_id(),
168 );
169 }
170171for req in &arg.r_ifs {
172if !!arg.is_required_set() {
{
::core::panicking::panic_fmt(format_args!("Argument {0}: `required` conflicts with `required_if_eq*`",
arg.get_id()));
}
};assert!(
173 !arg.is_required_set(),
174"Argument {}: `required` conflicts with `required_if_eq*`",
175 arg.get_id()
176 );
177if !cmd.id_exists(&req.0) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument or group \'{1}\' specified in \'required_if_eq*\' for \'{2}\' does not exist",
cmd.get_name(), req.0, arg.get_id()));
}
};assert!(
178 cmd.id_exists(&req.0),
179"Command {}: Argument or group '{}' specified in 'required_if_eq*' for '{}' does not exist",
180 cmd.get_name(),
181 req.0,
182 arg.get_id()
183 );
184 }
185186for req in &arg.r_ifs_all {
187if !!arg.is_required_set() {
{
::core::panicking::panic_fmt(format_args!("Argument {0}: `required` conflicts with `required_if_eq_all`",
arg.get_id()));
}
};assert!(
188 !arg.is_required_set(),
189"Argument {}: `required` conflicts with `required_if_eq_all`",
190 arg.get_id()
191 );
192if !cmd.id_exists(&req.0) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument or group \'{1}\' specified in \'required_if_eq_all\' for \'{2}\' does not exist",
cmd.get_name(), req.0, arg.get_id()));
}
};assert!(
193 cmd.id_exists(&req.0),
194"Command {}: Argument or group '{}' specified in 'required_if_eq_all' for '{}' does not exist",
195 cmd.get_name(),
196 req.0,
197 arg.get_id()
198 );
199 }
200201for req in &arg.r_unless {
202if !!arg.is_required_set() {
{
::core::panicking::panic_fmt(format_args!("Argument {0}: `required` conflicts with `required_unless*`",
arg.get_id()));
}
};assert!(
203 !arg.is_required_set(),
204"Argument {}: `required` conflicts with `required_unless*`",
205 arg.get_id()
206 );
207if !cmd.id_exists(req) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument or group \'{1}\' specified in \'required_unless*\' for \'{2}\' does not exist",
cmd.get_name(), req, arg.get_id()));
}
};assert!(
208 cmd.id_exists(req),
209"Command {}: Argument or group '{}' specified in 'required_unless*' for '{}' does not exist",
210 cmd.get_name(),
211 req,
212 arg.get_id(),
213 );
214 }
215216for req in &arg.r_unless_all {
217if !!arg.is_required_set() {
{
::core::panicking::panic_fmt(format_args!("Argument {0}: `required` conflicts with `required_unless*`",
arg.get_id()));
}
};assert!(
218 !arg.is_required_set(),
219"Argument {}: `required` conflicts with `required_unless*`",
220 arg.get_id()
221 );
222if !cmd.id_exists(req) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument or group \'{1}\' specified in \'required_unless*\' for \'{2}\' does not exist",
cmd.get_name(), req, arg.get_id()));
}
};assert!(
223 cmd.id_exists(req),
224"Command {}: Argument or group '{}' specified in 'required_unless*' for '{}' does not exist",
225 cmd.get_name(),
226 req,
227 arg.get_id(),
228 );
229 }
230231// blacklist
232for req in &arg.blacklist {
233if !cmd.id_exists(req) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument or group \'{1}\' specified in \'conflicts_with*\' for \'{2}\' does not exist",
cmd.get_name(), req, arg.get_id()));
}
};assert!(
234 cmd.id_exists(req),
235"Command {}: Argument or group '{}' specified in 'conflicts_with*' for '{}' does not exist",
236 cmd.get_name(),
237 req,
238 arg.get_id(),
239 );
240 }
241242// overrides
243for req in &arg.overrides {
244if !cmd.id_exists(req) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument or group \'{1}\' specified in \'overrides_with*\' for \'{2}\' does not exist",
cmd.get_name(), req, arg.get_id()));
}
};assert!(
245 cmd.id_exists(req),
246"Command {}: Argument or group '{}' specified in 'overrides_with*' for '{}' does not exist",
247 cmd.get_name(),
248 req,
249 arg.get_id(),
250 );
251 }
252253if arg.is_last_set() {
254if !arg.get_long().is_none() {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Flags or Options cannot have last(true) set. \'{1}\' has both a long and last(true) set.",
cmd.get_name(), arg.get_id()));
}
};assert!(
255 arg.get_long().is_none(),
256"Command {}: Flags or Options cannot have last(true) set. '{}' has both a long and last(true) set.",
257 cmd.get_name(),
258 arg.get_id()
259 );
260if !arg.get_short().is_none() {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Flags or Options cannot have last(true) set. \'{1}\' has both a short and last(true) set.",
cmd.get_name(), arg.get_id()));
}
};assert!(
261 arg.get_short().is_none(),
262"Command {}: Flags or Options cannot have last(true) set. '{}' has both a short and last(true) set.",
263 cmd.get_name(),
264 arg.get_id()
265 );
266 }
267268if !!(arg.is_required_set() && arg.is_global_set()) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Global arguments cannot be required.\n\n\t\'{1}\' is marked as both global and required",
cmd.get_name(), arg.get_id()));
}
};assert!(
269 !(arg.is_required_set() && arg.is_global_set()),
270"Command {}: Global arguments cannot be required.\n\n\t'{}' is marked as both global and required",
271 cmd.get_name(),
272 arg.get_id()
273 );
274275if arg.get_value_hint() == ValueHint::CommandWithArguments {
276if !arg.is_positional() {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument \'{1}\' has hint CommandWithArguments and must be positional.",
cmd.get_name(), arg.get_id()));
}
};assert!(
277 arg.is_positional(),
278"Command {}: Argument '{}' has hint CommandWithArguments and must be positional.",
279 cmd.get_name(),
280 arg.get_id()
281 );
282283if !(arg.is_trailing_var_arg_set() || arg.is_last_set()) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Positional argument \'{1}\' has hint CommandWithArguments, so Command must have `trailing_var_arg(true)` or `last(true)` set.",
cmd.get_name(), arg.get_id()));
}
};assert!(
284 arg.is_trailing_var_arg_set() || arg.is_last_set(),
285"Command {}: Positional argument '{}' has hint CommandWithArguments, so Command must have `trailing_var_arg(true)` or `last(true)` set.",
286 cmd.get_name(),
287 arg.get_id()
288 );
289 }
290 }
291292for group in cmd.get_groups() {
293// Name conflicts
294if !(cmd.get_groups().filter(|x| x.id == group.id).count() < 2) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument group name must be unique\n\n\t\'{1}\' is already in use",
cmd.get_name(), group.get_id()));
}
};assert!(
295 cmd.get_groups().filter(|x| x.id == group.id).count() < 2,
296"Command {}: Argument group name must be unique\n\n\t'{}' is already in use",
297 cmd.get_name(),
298 group.get_id(),
299 );
300301// Groups should not have naming conflicts with Args
302if !!cmd.get_arguments().any(|x| x.get_id() == group.get_id()) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument group name \'{1}\' must not conflict with argument name",
cmd.get_name(), group.get_id()));
}
};assert!(
303 !cmd.get_arguments().any(|x| x.get_id() == group.get_id()),
304"Command {}: Argument group name '{}' must not conflict with argument name",
305 cmd.get_name(),
306 group.get_id(),
307 );
308309for arg in &group.args {
310// Args listed inside groups should exist
311if !cmd.get_arguments().any(|x| x.get_id() == arg) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument group \'{1}\' contains non-existent argument \'{2}\'",
cmd.get_name(), group.get_id(), arg));
}
};assert!(
312 cmd.get_arguments().any(|x| x.get_id() == arg),
313"Command {}: Argument group '{}' contains non-existent argument '{}'",
314 cmd.get_name(),
315 group.get_id(),
316 arg
317 );
318 }
319320for arg in &group.requires {
321// Args listed inside groups should exist
322if !cmd.id_exists(arg) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument group \'{1}\' requires non-existent \'{2}\' id",
cmd.get_name(), group.get_id(), arg));
}
};assert!(
323 cmd.id_exists(arg),
324"Command {}: Argument group '{}' requires non-existent '{}' id",
325 cmd.get_name(),
326 group.get_id(),
327 arg
328 );
329 }
330331for arg in &group.conflicts {
332// Args listed inside groups should exist
333if !cmd.id_exists(arg) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: Argument group \'{1}\' conflicts with non-existent \'{2}\' id",
cmd.get_name(), group.get_id(), arg));
}
};assert!(
334 cmd.id_exists(arg),
335"Command {}: Argument group '{}' conflicts with non-existent '{}' id",
336 cmd.get_name(),
337 group.get_id(),
338 arg
339 );
340 }
341 }
342343// Conflicts between flags and subcommands
344345long_flags.sort_unstable();
346short_flags.sort_unstable();
347348detect_duplicate_flags(&long_flags, "long");
349detect_duplicate_flags(&short_flags, "short");
350351let mut subs = FlatSet::new();
352for sc in cmd.get_subcommands() {
353if !subs.insert(sc.get_name()) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: command name `{1}` is duplicated",
cmd.get_name(), sc.get_name()));
}
};assert!(
354 subs.insert(sc.get_name()),
355"Command {}: command name `{}` is duplicated",
356 cmd.get_name(),
357 sc.get_name()
358 );
359for alias in sc.get_all_aliases() {
360if !subs.insert(alias) {
{
::core::panicking::panic_fmt(format_args!("Command {0}: command `{1}` alias `{2}` is duplicated",
cmd.get_name(), sc.get_name(), alias));
}
};assert!(
361 subs.insert(alias),
362"Command {}: command `{}` alias `{}` is duplicated",
363 cmd.get_name(),
364 sc.get_name(),
365 alias
366 );
367 }
368 }
369370_verify_positionals(cmd);
371372#[cfg(feature = "help")]
373if let Some(help_template) = cmd.get_help_template() {
374if !!help_template.to_string().contains("{flags}") {
{
::core::panicking::panic_fmt(format_args!("Command {0}: {1}",
cmd.get_name(),
"`{flags}` template variable was removed in clap3, they are now included in `{options}`"));
}
};assert!(
375 !help_template.to_string().contains("{flags}"),
376"Command {}: {}",
377 cmd.get_name(),
378"`{flags}` template variable was removed in clap3, they are now included in `{options}`",
379 );
380if !!help_template.to_string().contains("{unified}") {
{
::core::panicking::panic_fmt(format_args!("Command {0}: {1}",
cmd.get_name(),
"`{unified}` template variable was removed in clap3, use `{options}` instead"));
}
};assert!(
381 !help_template.to_string().contains("{unified}"),
382"Command {}: {}",
383 cmd.get_name(),
384"`{unified}` template variable was removed in clap3, use `{options}` instead"
385);
386#[cfg(feature = "unstable-v5")]
387assert!(
388 !help_template.to_string().contains("{bin}"),
389"Command {}: {}",
390 cmd.get_name(),
391"`{bin}` template variable was removed in clap5, use `{name}` instead"
392);
393 }
394395cmd._panic_on_missing_help(cmd.is_help_expected_set());
396assert_app_flags(cmd);
397}
398399fn duplicate_tip(cmd: &Command, first: &Arg, second: &Arg) -> &'static str {
400if !cmd.is_disable_help_flag_set()
401 && (first.get_id() == Id::HELP || second.get_id() == Id::HELP)
402 {
403" (call `cmd.disable_help_flag(true)` to remove the auto-generated `--help`)"
404} else if !cmd.is_disable_version_flag_set()
405 && (first.get_id() == Id::VERSION || second.get_id() == Id::VERSION)
406 {
407" (call `cmd.disable_version_flag(true)` to remove the auto-generated `--version`)"
408} else {
409""
410}
411}
412413#[derive(#[automatically_derived]
impl<'a> ::core::cmp::Eq for Flag<'a> {
#[inline]
#[doc(hidden)]
#[coverage(off)]
fn assert_fields_are_eq(&self) {
let _: ::core::cmp::AssertParamIsEq<String>;
let _: ::core::cmp::AssertParamIsEq<&'a str>;
let _: ::core::cmp::AssertParamIsEq<&'a str>;
}
}Eq)]
414enum Flag<'a> {
415 Command(String, &'a str),
416 Arg(String, &'a str),
417}
418419impl PartialEqfor Flag<'_> {
420fn eq(&self, other: &Flag<'_>) -> bool {
421self.cmp(other) == Ordering::Equal422 }
423}
424425impl PartialOrdfor Flag<'_> {
426fn partial_cmp(&self, other: &Flag<'_>) -> Option<Ordering> {
427Some(self.cmp(other))
428 }
429}
430431impl Ordfor Flag<'_> {
432fn cmp(&self, other: &Self) -> Ordering {
433match (self, other) {
434 (Flag::Command(s1, _), Flag::Command(s2, _))
435 | (Flag::Arg(s1, _), Flag::Arg(s2, _))
436 | (Flag::Command(s1, _), Flag::Arg(s2, _))
437 | (Flag::Arg(s1, _), Flag::Command(s2, _)) => {
438if s1 == s2 {
439 Ordering::Equal440 } else {
441s1.cmp(s2)
442 }
443 }
444 }
445 }
446}
447448fn detect_duplicate_flags(flags: &[Flag<'_>], short_or_long: &str) {
449for (one, two) in find_duplicates(flags) {
450match (one, two) {
451 (Flag::Command(flag, one), Flag::Command(_, another)) if one != another => {
::core::panicking::panic_fmt(format_args!("the \'{0}\' {1} flag is specified for both \'{2}\' and \'{3}\' subcommands",
flag, short_or_long, one, another));
}panic!(
452"the '{flag}' {short_or_long} flag is specified for both '{one}' and '{another}' subcommands"
453),
454455 (Flag::Arg(flag, one), Flag::Arg(_, another)) if one != another => {
::core::panicking::panic_fmt(format_args!("{0} option names must be unique, but \'{1}\' is in use by both \'{2}\' and \'{3}\'",
short_or_long, flag, one, another));
}panic!(
456"{short_or_long} option names must be unique, but '{flag}' is in use by both '{one}' and '{another}'"
457),
458459 (Flag::Arg(flag, arg), Flag::Command(_, sub))
460 | (Flag::Command(flag, sub), Flag::Arg(_, arg)) => {
::core::panicking::panic_fmt(format_args!("the \'{0}\' {1} flag for the \'{2}\' argument conflicts with the short flag for \'{3}\' subcommand",
flag, short_or_long, arg, sub));
}panic!(
461"the '{flag}' {short_or_long} flag for the '{arg}' argument conflicts with the short flag \
462 for '{sub}' subcommand"
463),
464465_ => {}
466 }
467 }
468}
469470/// Find duplicates in a sorted array.
471///
472/// The algorithm is simple: the array is sorted, duplicates
473/// must be placed next to each other, we can check only adjacent elements.
474fn find_duplicates<T: PartialEq>(slice: &[T]) -> impl Iterator<Item = (&T, &T)> {
475slice.windows(2).filter_map(|w| {
476if w[0] == w[1] {
477Some((&w[0], &w[1]))
478 } else {
479None480 }
481 })
482}
483484fn assert_app_flags(cmd: &Command) {
485macro_rules! checker {
486 ($a:ident conflicts $($b:ident)|+) => {
487if cmd.$a() {
488let mut s = String::new();
489490 $(
491if cmd.$b() {
492use std::fmt::Write;
493write!(&mut s, " AppSettings::{} conflicts with AppSettings::{}.\n", std::stringify!($b), std::stringify!($a)).unwrap();
494 }
495 )+
496497if !s.is_empty() {
498panic!("{}\n{}", cmd.get_name(), s)
499 }
500 }
501 };
502 }
503504if cmd.is_multicall_set() {
let mut s = String::new();
if cmd.is_no_binary_name_set() {
use std::fmt::Write;
(&mut s).write_fmt(format_args!(" AppSettings::{0} conflicts with AppSettings::{1}.\n",
"is_no_binary_name_set", "is_multicall_set")).unwrap();
}
if !s.is_empty() {
{
::core::panicking::panic_fmt(format_args!("{0}\n{1}",
cmd.get_name(), s));
}
}
};checker!(is_multicall_set conflicts is_no_binary_name_set);
505}
506507#[cfg(debug_assertions)]
508fn _verify_positionals(cmd: &Command) -> bool {
509debug!("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)
516517let highest_idx = cmd518 .get_keymap()
519 .keys()
520 .filter_map(|x| {
521if let KeyType::Position(n) = x {
522Some(*n)
523 } else {
524None525 }
526 })
527 .max()
528 .unwrap_or(0);
529530let num_p = cmd.get_keymap().keys().filter(|x| x.is_position()).count();
531532if !(highest_idx == num_p) {
{
::core::panicking::panic_fmt(format_args!("Found positional argument whose index is {0} but there are only {1} positional arguments defined",
highest_idx, num_p));
}
};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 );
537538for arg in cmd.get_arguments() {
539if arg.index.unwrap_or(0) == highest_idx {
540if !(!arg.is_trailing_var_arg_set() || !arg.is_last_set()) {
{
::core::panicking::panic_fmt(format_args!("{0}:{1}: `Arg::trailing_var_arg` and `Arg::last` cannot be used together",
cmd.get_name(), arg.get_id()));
}
};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 );
546547if arg.is_trailing_var_arg_set() {
548if !arg.is_multiple() {
{
::core::panicking::panic_fmt(format_args!("{0}:{1}: `Arg::trailing_var_arg` must accept multiple values",
cmd.get_name(), arg.get_id()));
}
};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 {
556if !!arg.is_trailing_var_arg_set() {
{
::core::panicking::panic_fmt(format_args!("{0}:{1}: `Arg::trailing_var_arg` can only apply to last positional",
cmd.get_name(), arg.get_id()));
}
};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 }
564565// Next we verify that only the highest index has takes multiple arguments (if any)
566let only_highest = |a: &Arg| a.is_multiple() && (a.get_index().unwrap_or(0) != highest_idx);
567if 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
573574 // We can't pass the closure (it.next()) to the macro directly because each call to
575 // find() (iterator, not macro) gets called repeatedly.
576let last = &cmd.get_keymap()[&KeyType::Position(highest_idx)];
577let second_to_last = &cmd.get_keymap()[&KeyType::Position(highest_idx - 1)];
578579// Either the final positional is required
580 // Or the second to last has a terminator or .last(true) set
581let ok = last.is_required_set()
582 || (second_to_last.terminator.is_some() || second_to_last.is_last_set())
583 || last.is_last_set();
584if !ok {
{
::core::panicking::panic_fmt(format_args!("Positional argument `{0}` *must* have `required(true)` or `last(true)` set because a prior positional argument (`{1}`) has `num_args(1..)`",
last, second_to_last));
}
};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);
589590// We make sure if the second to last is Multiple the last is ArgSettings::Last
591let ok = second_to_last.is_multiple() || last.is_last_set();
592if !ok {
{
::core::panicking::panic_fmt(format_args!("Only the last positional argument, or second to last positional argument may be set to `.num_args(1..)`"));
}
};assert!(
593 ok,
594"Only the last positional argument, or second to last positional \
595 argument may be set to `.num_args(1..)`"
596);
597598// Next we check how many have both Multiple and not a specific number of values set
599let count = cmd600 .get_positionals()
601 .filter(|p| {
602p.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();
607let ok = count <= 1
608|| (last.is_last_set()
609 && last.is_multiple()
610 && second_to_last.is_multiple()
611 && count == 2);
612if !ok {
{
::core::panicking::panic_fmt(format_args!("Only one positional argument with `.num_args(1..)` set is allowed per command, unless the second one also has .last(true) set"));
}
};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 }
618619let mut found = false;
620621if 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.
624let mut foundx2 = false;
625626for p in cmd.get_positionals() {
627if foundx2 && !p.is_required_set() {
628if !p.is_required_set() {
{
::core::panicking::panic_fmt(format_args!("Found non-required positional argument with a lower index than a required positional argument by two or more: {0:?} index {1:?}",
p.get_id(), p.get_index()));
}
};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
643if found {
644 foundx2 = true;
645continue;
646 }
647 found = true;
648 } else {
649 found = false;
650 }
651 }
652 } else {
653// Check that if a required positional argument is found, all positions with a lower
654 // index are also required
655for p in (1..=num_p).rev().filter_map(|n| cmd.get_keymap().get(&n)) {
656if found {
657if !p.is_required_set() {
{
::core::panicking::panic_fmt(format_args!("Found non-required positional argument with a lower index than a required positional argument: {0:?} index {1:?}",
p.get_id(), p.get_index()));
}
};assert!(
658 p.is_required_set(),
659"Found non-required positional argument with a lower \
660 index than a required positional argument: {:?} index {:?}",
661 p.get_id(),
662 p.get_index()
663 );
664 } else if p.is_required_set() && !p.is_last_set() {
665// Args that .last(true) don't count since they can be required and have
666 // positionals with a lower index that aren't required
667 // Imagine: prog <req1> [opt1] -- <req2>
668 // Both of these are valid invocations:
669 // $ prog r1 -- r2
670 // $ prog r1 o1 -- r2
671found = true;
672 }
673 }
674 }
675if !(cmd.get_positionals().filter(|p| p.is_last_set()).count() < 2) {
{
::core::panicking::panic_fmt(format_args!("Only one positional argument may have last(true) set. Found two."));
}
};assert!(
676 cmd.get_positionals().filter(|p| p.is_last_set()).count() < 2,
677"Only one positional argument may have last(true) set. Found two."
678);
679if cmd680 .get_positionals()
681 .any(|p| p.is_last_set() && p.is_required_set())
682 && cmd.has_subcommands()
683 && !cmd.is_subcommand_negates_reqs_set()
684 {
685{
::core::panicking::panic_fmt(format_args!("Having a required positional argument with .last(true) set *and* child subcommands without setting SubcommandsNegateReqs isn\'t compatible."));
};panic!(
686"Having a required positional argument with .last(true) set *and* child \
687 subcommands without setting SubcommandsNegateReqs isn't compatible."
688);
689 }
690691true
692}
693694fn assert_arg(arg: &Arg) {
695debug!("Arg::_debug_asserts:{}", arg.get_id());
696697// Self conflict
698 // TODO: this check should be recursive
699if !!arg.blacklist.iter().any(|x| x == arg.get_id()) {
{
::core::panicking::panic_fmt(format_args!("Argument \'{0}\' cannot conflict with itself",
arg.get_id()));
}
};assert!(
700 !arg.blacklist.iter().any(|x| x == arg.get_id()),
701"Argument '{}' cannot conflict with itself",
702 arg.get_id(),
703 );
704705if !(arg.get_num_args().unwrap_or(1.into()).max_values() <=
arg.get_action().max_num_args().max_values()) {
{
::core::panicking::panic_fmt(format_args!("Argument `{0}`\'s action {1:?} is incompatible with `num_args({2:?})`",
arg.get_id(), arg.get_action(),
arg.get_num_args().unwrap_or(1.into())));
}
};assert!(
706 arg.get_num_args().unwrap_or(1.into()).max_values()
707 <= arg.get_action().max_num_args().max_values(),
708"Argument `{}`'s action {:?} is incompatible with `num_args({:?})`",
709 arg.get_id(),
710 arg.get_action(),
711 arg.get_num_args().unwrap_or(1.into())
712 );
713if let Some(action_type_id) = arg.get_action().value_type_id() {
714match (&action_type_id, &arg.get_value_parser().type_id()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::Some(format_args!("Argument `{0}`\'s selected action {1:?} contradicts `value_parser` ({2:?})",
arg.get_id(), arg.get_action(), arg.get_value_parser())));
}
}
};assert_eq!(
715 action_type_id,
716 arg.get_value_parser().type_id(),
717"Argument `{}`'s selected action {:?} contradicts `value_parser` ({:?})",
718 arg.get_id(),
719 arg.get_action(),
720 arg.get_value_parser()
721 );
722 }
723724if arg.get_value_hint() != ValueHint::Unknown {
725if !arg.is_takes_value_set() {
{
::core::panicking::panic_fmt(format_args!("Argument \'{0}\' has value hint but takes no value",
arg.get_id()));
}
};assert!(
726 arg.is_takes_value_set(),
727"Argument '{}' has value hint but takes no value",
728 arg.get_id()
729 );
730731if arg.get_value_hint() == ValueHint::CommandWithArguments {
732if !arg.is_multiple_values_set() {
{
::core::panicking::panic_fmt(format_args!("Argument \'{0}\' uses hint CommandWithArguments and must accept multiple values",
arg.get_id()));
}
};assert!(
733 arg.is_multiple_values_set(),
734"Argument '{}' uses hint CommandWithArguments and must accept multiple values",
735 arg.get_id()
736 );
737 }
738 }
739740if arg.index.is_some() {
741if !arg.is_positional() {
{
::core::panicking::panic_fmt(format_args!("Argument \'{0}\' is a positional argument and can\'t have short or long name versions",
arg.get_id()));
}
};assert!(
742 arg.is_positional(),
743"Argument '{}' is a positional argument and can't have short or long name versions",
744 arg.get_id()
745 );
746if !arg.is_takes_value_set() {
{
::core::panicking::panic_fmt(format_args!("Argument \'{0}\' is positional and it must take a value but action is {1:?}{2}",
arg.get_id(), arg.get_action(),
if arg.get_id() == Id::HELP {
" (`mut_arg` no longer works with implicit `--help`)"
} else if arg.get_id() == Id::VERSION {
" (`mut_arg` no longer works with implicit `--version`)"
} else { "" }));
}
};assert!(
747 arg.is_takes_value_set(),
748"Argument '{}' is positional and it must take a value but action is {:?}{}",
749 arg.get_id(),
750 arg.get_action(),
751if 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 }
760761let num_vals = arg.get_num_args().expect(INTERNAL_ERROR_MSG);
762// This can be the cause of later asserts, so put this first
763if num_vals != ValueRange::EMPTY {
764// HACK: Don't check for flags to make the derive easier
765let num_val_names = arg.get_value_names().unwrap_or(&[]).len();
766if num_vals.max_values() < num_val_names {
767{
::core::panicking::panic_fmt(format_args!("Argument {0}: Too many value names ({1}) compared to `num_args` ({2})",
arg.get_id(), num_val_names, num_vals));
};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 }
775776match (&num_vals.is_multiple(), &arg.is_multiple_values_set()) {
(left_val, right_val) => {
if !(*left_val == *right_val) {
let kind = ::core::panicking::AssertKind::Eq;
::core::panicking::assert_failed(kind, &*left_val, &*right_val,
::core::option::Option::Some(format_args!("Argument {0}: mismatch between `num_args` ({1}) and `multiple_values`",
arg.get_id(), num_vals)));
}
}
};assert_eq!(
777 num_vals.is_multiple(),
778 arg.is_multiple_values_set(),
779"Argument {}: mismatch between `num_args` ({}) and `multiple_values`",
780 arg.get_id(),
781 num_vals,
782 );
783784if 1 < num_vals.min_values() {
785if !!arg.is_require_equals_set() {
{
::core::panicking::panic_fmt(format_args!("Argument {0}: cannot accept more than 1 arg (num_args={1}) with require_equals",
arg.get_id(), num_vals));
}
};assert!(
786 !arg.is_require_equals_set(),
787"Argument {}: cannot accept more than 1 arg (num_args={}) with require_equals",
788 arg.get_id(),
789 num_vals
790 );
791 }
792793if num_vals == ValueRange::SINGLE {
794if !!arg.is_multiple_values_set() {
{
::core::panicking::panic_fmt(format_args!("Argument {0}: mismatch between `num_args` and `multiple_values`",
arg.get_id()));
}
};assert!(
795 !arg.is_multiple_values_set(),
796"Argument {}: mismatch between `num_args` and `multiple_values`",
797 arg.get_id()
798 );
799 }
800801assert_arg_flags(arg);
802}
803804fn assert_arg_flags(arg: &Arg) {
805macro_rules! checker {
806 ($a:ident requires $($b:ident)|+) => {
807if arg.$a() {
808let mut s = String::new();
809810 $(
811if !arg.$b() {
812use std::fmt::Write;
813write!(&mut s, " Arg::{} is required when Arg::{} is set.\n", std::stringify!($b), std::stringify!($a)).unwrap();
814 }
815 )+
816817if !s.is_empty() {
818panic!("Argument {:?}\n{}", arg.get_id(), s)
819 }
820 }
821 }
822 }
823824if arg.is_hide_possible_values_set() {
let mut s = String::new();
if !arg.is_takes_value_set() {
use std::fmt::Write;
(&mut s).write_fmt(format_args!(" Arg::{0} is required when Arg::{1} is set.\n",
"is_takes_value_set",
"is_hide_possible_values_set")).unwrap();
}
if !s.is_empty() {
{
::core::panicking::panic_fmt(format_args!("Argument {0:?}\n{1}",
arg.get_id(), s));
}
}
};checker!(is_hide_possible_values_set requires is_takes_value_set);
825if arg.is_allow_hyphen_values_set() {
let mut s = String::new();
if !arg.is_takes_value_set() {
use std::fmt::Write;
(&mut s).write_fmt(format_args!(" Arg::{0} is required when Arg::{1} is set.\n",
"is_takes_value_set",
"is_allow_hyphen_values_set")).unwrap();
}
if !s.is_empty() {
{
::core::panicking::panic_fmt(format_args!("Argument {0:?}\n{1}",
arg.get_id(), s));
}
}
};checker!(is_allow_hyphen_values_set requires is_takes_value_set);
826if arg.is_allow_negative_numbers_set() {
let mut s = String::new();
if !arg.is_takes_value_set() {
use std::fmt::Write;
(&mut s).write_fmt(format_args!(" Arg::{0} is required when Arg::{1} is set.\n",
"is_takes_value_set",
"is_allow_negative_numbers_set")).unwrap();
}
if !s.is_empty() {
{
::core::panicking::panic_fmt(format_args!("Argument {0:?}\n{1}",
arg.get_id(), s));
}
}
};checker!(is_allow_negative_numbers_set requires is_takes_value_set);
827if arg.is_require_equals_set() {
let mut s = String::new();
if !arg.is_takes_value_set() {
use std::fmt::Write;
(&mut s).write_fmt(format_args!(" Arg::{0} is required when Arg::{1} is set.\n",
"is_takes_value_set", "is_require_equals_set")).unwrap();
}
if !s.is_empty() {
{
::core::panicking::panic_fmt(format_args!("Argument {0:?}\n{1}",
arg.get_id(), s));
}
}
};checker!(is_require_equals_set requires is_takes_value_set);
828if arg.is_last_set() {
let mut s = String::new();
if !arg.is_takes_value_set() {
use std::fmt::Write;
(&mut s).write_fmt(format_args!(" Arg::{0} is required when Arg::{1} is set.\n",
"is_takes_value_set", "is_last_set")).unwrap();
}
if !s.is_empty() {
{
::core::panicking::panic_fmt(format_args!("Argument {0:?}\n{1}",
arg.get_id(), s));
}
}
};checker!(is_last_set requires is_takes_value_set);
829if arg.is_hide_default_value_set() {
let mut s = String::new();
if !arg.is_takes_value_set() {
use std::fmt::Write;
(&mut s).write_fmt(format_args!(" Arg::{0} is required when Arg::{1} is set.\n",
"is_takes_value_set",
"is_hide_default_value_set")).unwrap();
}
if !s.is_empty() {
{
::core::panicking::panic_fmt(format_args!("Argument {0:?}\n{1}",
arg.get_id(), s));
}
}
};checker!(is_hide_default_value_set requires is_takes_value_set);
830if arg.is_multiple_values_set() {
let mut s = String::new();
if !arg.is_takes_value_set() {
use std::fmt::Write;
(&mut s).write_fmt(format_args!(" Arg::{0} is required when Arg::{1} is set.\n",
"is_takes_value_set", "is_multiple_values_set")).unwrap();
}
if !s.is_empty() {
{
::core::panicking::panic_fmt(format_args!("Argument {0:?}\n{1}",
arg.get_id(), s));
}
}
};checker!(is_multiple_values_set requires is_takes_value_set);
831if arg.is_ignore_case_set() {
let mut s = String::new();
if !arg.is_takes_value_set() {
use std::fmt::Write;
(&mut s).write_fmt(format_args!(" Arg::{0} is required when Arg::{1} is set.\n",
"is_takes_value_set", "is_ignore_case_set")).unwrap();
}
if !s.is_empty() {
{
::core::panicking::panic_fmt(format_args!("Argument {0:?}\n{1}",
arg.get_id(), s));
}
}
};checker!(is_ignore_case_set requires is_takes_value_set);
832}