forked from clap-rs/clap
/
debug_asserts.rs
136 lines (120 loc) · 4.15 KB
/
debug_asserts.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use crate::{Arg, ArgSettings, ValueHint};
pub(crate) fn assert_arg(arg: &Arg) {
debug!("Arg::_debug_asserts:{}", arg.name);
// Self conflict
// TODO: this check should be recursive
assert!(
!arg.blacklist.iter().any(|x| *x == arg.id),
"Argument '{}' cannot conflict with itself",
arg.name,
);
if arg.value_hint != ValueHint::Unknown {
assert!(
arg.is_set(ArgSettings::TakesValue),
"Argument '{}' has value hint but takes no value",
arg.name
);
if arg.value_hint == ValueHint::CommandWithArguments {
assert!(
arg.is_set(ArgSettings::MultipleValues),
"Argument '{}' uses hint CommandWithArguments and must accept multiple values",
arg.name
)
}
}
if arg.index.is_some() {
assert!(
arg.is_positional(),
"Argument '{}' is a positional argument and can't have short or long name versions",
arg.name
);
}
if arg.is_set(ArgSettings::Required) {
assert!(
arg.default_vals.is_empty(),
"Argument '{}' is required and can't have a default value",
arg.name
);
}
assert_arg_flags(arg);
assert_defaults(arg, "default_value", arg.default_vals.iter().copied());
assert_defaults(
arg,
"default_missing_value",
arg.default_missing_vals.iter().copied(),
);
assert_defaults(
arg,
"default_value_if",
arg.default_vals_ifs
.iter()
.filter_map(|(_, _, default)| *default),
);
}
fn assert_arg_flags(arg: &Arg) {
use ArgSettings::*;
macro_rules! checker {
($a:ident requires $($b:ident)|+) => {
if arg.is_set($a) {
let mut s = String::new();
$(
if !arg.is_set($b) {
s.push_str(&format!(" ArgSettings::{} is required when ArgSettings::{} is set.\n", std::stringify!($b), std::stringify!($a)));
}
)+
if !s.is_empty() {
panic!("Argument {:?}\n{}", arg.get_name(), s)
}
}
}
}
checker!(ForbidEmptyValues requires TakesValue);
checker!(RequireDelimiter requires TakesValue | UseValueDelimiter);
checker!(HidePossibleValues requires TakesValue);
checker!(AllowHyphenValues requires TakesValue);
checker!(RequireEquals requires TakesValue);
checker!(Last requires TakesValue);
checker!(HideDefaultValue requires TakesValue);
checker!(MultipleValues requires TakesValue);
checker!(IgnoreCase requires TakesValue);
checker!(AllowInvalidUtf8 requires TakesValue);
}
fn assert_defaults<'d>(
arg: &Arg,
field: &'static str,
defaults: impl IntoIterator<Item = &'d std::ffi::OsStr>,
) {
for default_os in defaults {
if let Some(default_s) = default_os.to_str() {
if !arg.possible_vals.is_empty() {
assert!(
arg.possible_vals.iter().any(|possible_val| {
possible_val.matches(default_s, arg.is_set(ArgSettings::IgnoreCase))
}),
"Argument `{}`'s {}={} doesn't match possible values",
arg.name,
field,
default_s
);
}
if let Some(validator) = arg.validator.as_ref() {
let mut validator = validator.lock().unwrap();
if let Err(err) = validator(default_s) {
panic!(
"Argument `{}`'s {}={} failed validation: {}",
arg.name, field, default_s, err
);
}
}
}
if let Some(validator) = arg.validator_os.as_ref() {
let mut validator = validator.lock().unwrap();
if let Err(err) = validator(default_os) {
panic!(
"Argument `{}`'s {}={:?} failed validation: {}",
arg.name, field, default_os, err
);
}
}
}
}