1use std::env;
13
14use crate::error::*;
15
16#[derive(Clone, Copy, Debug, PartialEq)]
19enum FlagType {
20 Pass(bool),
23 Drop(bool),
26 Error(&'static str),
29}
30
31static KNOWN_FLAGS: &[(&str, FlagType)] = &[
36 ("--bench", FlagType::Pass(false)),
37 ("--color", FlagType::Pass(true)),
38 ("--ensure-time", FlagType::Drop(false)),
39 ("--exact", FlagType::Drop(false)),
40 ("--exclude-should-panic", FlagType::Pass(false)),
41 ("--force-run-in-process", FlagType::Pass(false)),
42 ("--format", FlagType::Drop(true)),
43 ("--help", FlagType::Error("Tests run but --help passed to process?")),
44 ("--ignored", FlagType::Pass(false)),
45 ("--include-ignored", FlagType::Pass(false)),
46 ("--list", FlagType::Error("Tests run but --list passed to process?")),
47 ("--logfile", FlagType::Drop(true)),
48 ("--nocapture", FlagType::Drop(true)),
49 ("--quiet", FlagType::Drop(false)),
50 ("--report-time", FlagType::Drop(true)),
51 ("--show-output", FlagType::Pass(false)),
52 ("--skip", FlagType::Drop(true)),
53 ("--test", FlagType::Pass(false)),
54 ("--test-threads", FlagType::Drop(true)),
55 ("-Z", FlagType::Pass(true)),
56 ("-h", FlagType::Error("Tests run but -h passed to process?")),
57 ("-q", FlagType::Drop(false)),
58];
59
60fn look_up_flag_from_table(flag: &str) -> Option<FlagType> {
61 KNOWN_FLAGS.iter().cloned().filter(|&(name, _)| name == flag)
62 .map(|(_, typ)| typ).next()
63}
64
65pub(crate) fn env_var_for_flag(flag: &str) -> String {
66 let mut var = "RUSTY_FORK_FLAG_".to_owned();
67 var.push_str(
68 &flag.trim_start_matches('-').to_uppercase().replace('-', "_"));
69 var
70}
71
72fn look_up_flag_from_env(flag: &str) -> Option<FlagType> {
73 env::var(&env_var_for_flag(flag)).ok().map(
74 |value| match &*value {
75 "pass" => FlagType::Pass(false),
76 "pass-arg" => FlagType::Pass(true),
77 "drop" => FlagType::Drop(false),
78 "drop-arg" => FlagType::Drop(true),
79 _ => FlagType::Error("incorrect flag type in environment; \
80 must be one of `pass`, `pass-arg`, \
81 `drop`, `drop-arg`"),
82 })
83}
84
85fn look_up_flag(flag: &str) -> Option<FlagType> {
86 look_up_flag_from_table(flag).or_else(|| look_up_flag_from_env(flag))
87}
88
89fn look_up_flag_or_err(flag: &str) -> Result<(bool, bool)> {
90 match look_up_flag(flag) {
91 None =>
92 Err(Error::UnknownFlag(flag.to_owned())),
93 Some(FlagType::Error(message)) =>
94 Err(Error::DisallowedFlag(flag.to_owned(), message.to_owned())),
95 Some(FlagType::Pass(has_arg)) => Ok((true, has_arg)),
96 Some(FlagType::Drop(has_arg)) => Ok((false, has_arg)),
97 }
98}
99
100pub(crate) fn strip_cmdline<A : Iterator<Item = String>>
107 (args: A) -> Result<Vec<String>>
108{
109 #[derive(Clone, Copy)]
110 enum State {
111 Ground, PassingArg, DroppingArg,
112 }
113
114 let mut state = State::DroppingArg;
116 let mut ret = Vec::new();
117
118 for arg in args {
119 match state {
120 State::DroppingArg => {
121 state = State::Ground;
122 },
123
124 State::PassingArg => {
125 ret.push(arg);
126 state = State::Ground;
127 },
128
129 State::Ground => {
130 if &arg == "--" {
131 break;
133 } else if &arg == "-" {
134 continue;
136 } else if arg.starts_with("--") {
137 let (pass, has_arg) = look_up_flag_or_err(
138 arg.split('=').next().expect("split returned empty"))?;
139 let has_arg = has_arg && !arg.contains('=');
143 if pass {
144 ret.push(arg);
145 if has_arg {
146 state = State::PassingArg;
147 }
148 } else if has_arg {
149 state = State::DroppingArg;
150 }
151 } else if arg.starts_with("-") {
152 let mut chars = arg.chars();
153 let mut to_pass = "-".to_owned();
154
155 chars.next(); while let Some(flag_ch) = chars.next() {
157 let flag = format!("-{}", flag_ch);
158 let (pass, has_arg) = look_up_flag_or_err(&flag)?;
159 if pass {
160 to_pass.push(flag_ch);
161 if has_arg {
162 if chars.clone().next().is_some() {
163 to_pass.extend(chars);
165 } else {
166 state = State::PassingArg;
168 }
169 break;
170 }
171 } else if has_arg {
172 if chars.clone().next().is_none() {
173 state = State::DroppingArg;
175 }
176 break;
177 }
178 }
179
180 if "-" != &to_pass {
181 ret.push(to_pass);
182 }
183 } else {
184 }
186 },
187 }
188 }
189
190 Ok(ret)
191}
192
193pub(crate) static RUN_TEST_ARGS: &[&str] = &[
196 "--quiet",
198 "--test-threads", "1",
200 "--nocapture",
203 "--exact",
205 "--",
207];
208
209#[cfg(test)]
210mod test {
211 use super::*;
212
213 fn strip(cmdline: &str) -> Result<String> {
214 strip_cmdline(cmdline.split_whitespace().map(|s| s.to_owned()))
215 .map(|strs| strs.join(" "))
216 }
217
218 #[test]
219 fn test_strip() {
220 assert_eq!("", &strip("test").unwrap());
221 assert_eq!("--ignored", &strip("test --ignored").unwrap());
222 assert_eq!("", &strip("test --quiet").unwrap());
223 assert_eq!("", &strip("test -q").unwrap());
224 assert_eq!("", &strip("test -qq").unwrap());
225 assert_eq!("", &strip("test --test-threads 42").unwrap());
226 assert_eq!("-Z unstable-options",
227 &strip("test -Z unstable-options").unwrap());
228 assert_eq!("-Zunstable-options",
229 &strip("test -Zunstable-options").unwrap());
230 assert_eq!("-Zunstable-options",
231 &strip("test -qZunstable-options").unwrap());
232 assert_eq!("--color auto", &strip("test --color auto").unwrap());
233 assert_eq!("--color=auto", &strip("test --color=auto").unwrap());
234 assert_eq!("", &strip("test filter filter2").unwrap());
235 assert_eq!("", &strip("test -- --color=auto").unwrap());
236
237 match strip("test --plugh").unwrap_err() {
238 Error::UnknownFlag(ref flag) => assert_eq!("--plugh", flag),
239 e => panic!("Unexpected error: {}", e),
240 }
241 match strip("test --help").unwrap_err() {
242 Error::DisallowedFlag(ref flag, _) => assert_eq!("--help", flag),
243 e => panic!("Unexpected error: {}", e),
244 }
245 }
246
247 rusty_fork_test! {
250 #[test]
251 fn define_args_via_env() {
252 env::set_var("RUSTY_FORK_FLAG_X", "pass");
253 env::set_var("RUSTY_FORK_FLAG_FOO", "pass-arg");
254 env::set_var("RUSTY_FORK_FLAG_BAR", "drop");
255 env::set_var("RUSTY_FORK_FLAG_BAZ", "drop-arg");
256
257 assert_eq!("-X", &strip("test -X foo").unwrap());
258 assert_eq!("--foo bar", &strip("test --foo bar").unwrap());
259 assert_eq!("", &strip("test --bar").unwrap());
260 assert_eq!("", &strip("test --baz --notaflag").unwrap());
261 }
262 }
263}