1use crate::string::{EnvChar, EnvStr};
2use crate::{Flag, RustFlags};
3use std::str;
4
5enum FlagConstructor {
6 Flag(Flag),
7 Opt(fn(&EnvStr) -> Option<Flag>),
8 Repeated(fn(&EnvStr) -> Option<(Flag, usize)>),
9 Unrecognized,
10}
11
12mod opt {
13 use crate::string::EnvStr;
14 use crate::{
15 Color, CrateType, Emit, ErrorFormat, Flag, LibraryKind, LinkKind, LinkModifier,
16 LinkModifierPrefix, LintLevel,
17 };
18 use std::ffi::OsString;
19 use std::mem;
20 use std::path::PathBuf;
21
22 pub(crate) fn cfg(arg: &EnvStr) -> Option<Flag> {
23 let arg = arg.to_str()?;
24 let (name, value) = match arg.split_once('=') {
25 Some((name, value)) => {
26 let len = value.len();
27 if len >= 2 && value.starts_with('"') && value[1..].find('"') == Some(len - 2) {
28 (name, Some(&value[1..len - 1]))
29 } else {
30 return None;
31 }
32 }
33 None => (arg, None),
34 };
35 let name = name.to_owned();
36 let value = value.map(str::to_owned);
37 Some(Flag::Cfg { name, value })
38 }
39
40 pub(crate) fn library_search_path(arg: &EnvStr) -> Option<Flag> {
41 let (kind, path) = if let Some((kind, path)) = arg.split_once('=') {
42 let kind = match kind.to_str()? {
43 "dependency" => LibraryKind::Dependency,
44 "crate" => LibraryKind::Crate,
45 "native" => LibraryKind::Native,
46 "framework" => LibraryKind::Framework,
47 "all" => LibraryKind::All,
48 _ => return None,
49 };
50 (kind, path)
51 } else {
52 (LibraryKind::All, arg)
53 };
54 let path = PathBuf::from(path);
55 Some(Flag::LibrarySearchPath { kind, path })
56 }
57
58 pub(crate) fn link(arg: &EnvStr) -> Option<Flag> {
59 let arg = arg.to_str()?;
60 let mut modifiers = Vec::new();
61 let (kind, name) = match arg.split_once('=') {
62 Some((mut kind, name)) => {
63 if let Some((modified_kind, comma_separated_modifiers)) = kind.split_once(':') {
64 for modifier in comma_separated_modifiers.split(',') {
65 let prefix = match modifier.chars().next() {
66 Some('+') => LinkModifierPrefix::Enable,
67 Some('-') => LinkModifierPrefix::Disable,
68 _ => continue,
69 };
70 let modifier = match &modifier[1..] {
71 "bundle" => LinkModifier::Bundle,
72 "verbatim" => LinkModifier::Verbatim,
73 "whole-archive" => LinkModifier::WholeArchive,
74 "as-needed" => LinkModifier::AsNeeded,
75 _ => continue,
76 };
77 modifiers.push((prefix, modifier));
78 }
79 kind = modified_kind;
80 }
81 let kind = match kind {
82 "static" => LinkKind::Static,
83 "framework" => LinkKind::Framework,
84 "dylib" => LinkKind::Dylib,
85 _ => return None,
86 };
87 (kind, name)
88 }
89 None => (LinkKind::Dylib, arg),
90 };
91 let (name, rename) = match name.split_once(':') {
92 Some((name, rename)) => (name, Some(rename)),
93 None => (name, None),
94 };
95 let name = name.to_owned();
96 let rename = rename.map(str::to_owned);
97 Some(Flag::Link {
98 kind,
99 modifiers,
100 name,
101 rename,
102 })
103 }
104
105 pub(crate) fn crate_type(mut arg: &EnvStr) -> Option<(Flag, usize)> {
106 while !arg.is_empty() {
107 let first = match arg.split_once(',') {
108 Some((first, rest)) => {
109 arg = rest;
110 first
111 }
112 None => mem::take(&mut arg),
113 };
114 let Some(first) = first.to_str() else {
115 continue;
116 };
117 let crate_type = match first {
118 "bin" => CrateType::Bin,
119 "lib" => CrateType::Lib,
120 "rlib" => CrateType::Rlib,
121 "dylib" => CrateType::Dylib,
122 "cdylib" => CrateType::Cdylib,
123 "staticlib" => CrateType::Staticlib,
124 "proc-macro" => CrateType::ProcMacro,
125 _ => continue,
126 };
127 return Some((Flag::CrateType(crate_type), arg.len()));
128 }
129 None
130 }
131
132 pub(crate) fn crate_name(arg: &EnvStr) -> Option<Flag> {
133 let arg = arg.to_str()?;
134 Some(Flag::CrateName(arg.to_owned()))
135 }
136
137 pub(crate) fn edition(arg: &EnvStr) -> Option<Flag> {
138 let arg = arg.to_str()?;
139 arg.parse().ok().map(Flag::Edition)
140 }
141
142 pub(crate) fn emit(mut arg: &EnvStr) -> Option<(Flag, usize)> {
143 while !arg.is_empty() {
144 let first = match arg.split_once(',') {
145 Some((first, rest)) => {
146 arg = rest;
147 first
148 }
149 None => mem::take(&mut arg),
150 };
151 let Some(first) = first.to_str() else {
152 continue;
153 };
154 let emit = match first {
155 "asm" => Emit::Asm,
156 "llvm-bc" => Emit::LlvmBc,
157 "llvm-ir" => Emit::LlvmIr,
158 "obj" => Emit::Obj,
159 "metadata" => Emit::Metadata,
160 "link" => Emit::Link,
161 "dep-info" => Emit::DepInfo,
162 "mir" => Emit::Mir,
163 _ => continue,
164 };
165 return Some((Flag::Emit(emit), arg.len()));
166 }
167 None
168 }
169
170 pub(crate) fn print(arg: &EnvStr) -> Option<Flag> {
171 let arg = arg.to_str()?;
172 Some(Flag::Print(arg.to_owned()))
173 }
174
175 pub(crate) fn out(arg: &EnvStr) -> Option<Flag> {
176 Some(Flag::Out(PathBuf::from(arg)))
177 }
178
179 pub(crate) fn out_dir(arg: &EnvStr) -> Option<Flag> {
180 Some(Flag::OutDir(PathBuf::from(arg)))
181 }
182
183 pub(crate) fn explain(arg: &EnvStr) -> Option<Flag> {
184 let arg = arg.to_str()?;
185 Some(Flag::Explain(arg.to_owned()))
186 }
187
188 pub(crate) fn target(arg: &EnvStr) -> Option<Flag> {
189 let arg = arg.to_str()?;
190 Some(Flag::Target(arg.to_owned()))
191 }
192
193 pub(crate) fn allow(arg: &EnvStr) -> Option<Flag> {
194 let arg = arg.to_str()?;
195 Some(Flag::Allow(arg.to_owned()))
196 }
197
198 pub(crate) fn warn(arg: &EnvStr) -> Option<Flag> {
199 let arg = arg.to_str()?;
200 Some(Flag::Warn(arg.to_owned()))
201 }
202
203 pub(crate) fn force_warn(arg: &EnvStr) -> Option<Flag> {
204 let arg = arg.to_str()?;
205 Some(Flag::ForceWarn(arg.to_owned()))
206 }
207
208 pub(crate) fn deny(arg: &EnvStr) -> Option<Flag> {
209 let arg = arg.to_str()?;
210 Some(Flag::Deny(arg.to_owned()))
211 }
212
213 pub(crate) fn forbid(arg: &EnvStr) -> Option<Flag> {
214 let arg = arg.to_str()?;
215 Some(Flag::Forbid(arg.to_owned()))
216 }
217
218 pub(crate) fn cap_lints(arg: &EnvStr) -> Option<Flag> {
219 let arg = arg.to_str()?;
220 let level = match arg {
221 "allow" => LintLevel::Allow,
222 "warn" => LintLevel::Warn,
223 "deny" => LintLevel::Deny,
224 "forbid" => LintLevel::Forbid,
225 _ => return None,
226 };
227 Some(Flag::CapLints(level))
228 }
229
230 pub(crate) fn codegen(arg: &EnvStr) -> Option<Flag> {
231 let arg = arg.to_str()?;
232 let (opt, value) = match arg.split_once('=') {
233 Some((opt, value)) => (opt, Some(value)),
234 None => (arg, None),
235 };
236 let opt = opt.to_owned();
237 let value = value.map(str::to_owned);
238 Some(Flag::Codegen { opt, value })
239 }
240
241 pub(crate) fn extern_(arg: &EnvStr) -> Option<Flag> {
242 let (name, path) = match arg.split_once('=') {
243 Some((name, path)) => (name, Some(path)),
244 None => (arg, None),
245 };
246 let name = name.to_str()?.to_owned();
247 let path = path.map(PathBuf::from);
248 Some(Flag::Extern { name, path })
249 }
250
251 pub(crate) fn extern_location(arg: &EnvStr) -> Option<Flag> {
252 let (name, location) = arg.split_once('=')?;
253 let name = name.to_str()?.to_owned();
254 let location = OsString::from(location);
255 Some(Flag::ExternLocation { name, location })
256 }
257
258 pub(crate) fn sysroot(arg: &EnvStr) -> Option<Flag> {
259 Some(Flag::Sysroot(PathBuf::from(arg)))
260 }
261
262 pub(crate) fn z(arg: &EnvStr) -> Option<Flag> {
263 let arg = arg.to_str()?;
264 Some(Flag::Z(arg.to_owned()))
265 }
266
267 pub(crate) fn error_format(arg: &EnvStr) -> Option<Flag> {
268 let arg = arg.to_str()?;
269 let format = match arg {
270 "human" => ErrorFormat::Human,
271 "json" => ErrorFormat::Json,
272 "short" => ErrorFormat::Short,
273 _ => return None,
274 };
275 Some(Flag::ErrorFormat(format))
276 }
277
278 pub(crate) fn json(arg: &EnvStr) -> Option<Flag> {
279 let arg = arg.to_str()?;
280 Some(Flag::Json(arg.to_owned()))
281 }
282
283 pub(crate) fn color(arg: &EnvStr) -> Option<Flag> {
284 let arg = arg.to_str()?;
285 let color = match arg {
286 "auto" => Color::Auto,
287 "always" => Color::Always,
288 "never" => Color::Never,
289 _ => return None,
290 };
291 Some(Flag::Color(color))
292 }
293
294 pub(crate) fn remap_path_prefix(arg: &EnvStr) -> Option<Flag> {
295 let (from, to) = arg.split_once('=')?;
296 let from = PathBuf::from(from);
297 let to = PathBuf::from(to);
298 Some(Flag::RemapPathPrefix { from, to })
299 }
300}
301
302fn lookup_short(ch: char) -> FlagConstructor {
303 match ch {
304 'h' => FlagConstructor::Flag(Flag::Help),
305 'L' => FlagConstructor::Opt(opt::library_search_path),
306 'l' => FlagConstructor::Opt(opt::link),
307 'g' => FlagConstructor::Flag(Flag::Codegen {
308 opt: "debuginfo".to_owned(),
309 value: Some("2".to_owned()),
310 }),
311 'O' => FlagConstructor::Flag(Flag::Codegen {
312 opt: "opt-level".to_owned(),
313 value: Some("2".to_owned()),
314 }),
315 'o' => FlagConstructor::Opt(opt::out),
316 'A' => FlagConstructor::Opt(opt::allow),
317 'W' => FlagConstructor::Opt(opt::warn),
318 'D' => FlagConstructor::Opt(opt::deny),
319 'F' => FlagConstructor::Opt(opt::forbid),
320 'C' => FlagConstructor::Opt(opt::codegen),
321 'V' => FlagConstructor::Flag(Flag::Version),
322 'v' => FlagConstructor::Flag(Flag::Verbose),
323 'Z' => FlagConstructor::Opt(opt::z),
324 _ => FlagConstructor::Unrecognized,
325 }
326}
327
328fn lookup_long(name: &str) -> FlagConstructor {
329 match name {
330 "help" => FlagConstructor::Flag(Flag::Help),
331 "cfg" => FlagConstructor::Opt(opt::cfg),
332 "crate-type" => FlagConstructor::Repeated(opt::crate_type),
333 "crate-name" => FlagConstructor::Opt(opt::crate_name),
334 "edition" => FlagConstructor::Opt(opt::edition),
335 "emit" => FlagConstructor::Repeated(opt::emit),
336 "print" => FlagConstructor::Opt(opt::print),
337 "out-dir" => FlagConstructor::Opt(opt::out_dir),
338 "explain" => FlagConstructor::Opt(opt::explain),
339 "test" => FlagConstructor::Flag(Flag::Test),
340 "target" => FlagConstructor::Opt(opt::target),
341 "allow" => FlagConstructor::Opt(opt::allow),
342 "warn" => FlagConstructor::Opt(opt::warn),
343 "force-warn" => FlagConstructor::Opt(opt::force_warn),
344 "deny" => FlagConstructor::Opt(opt::deny),
345 "forbid" => FlagConstructor::Opt(opt::forbid),
346 "cap-lints" => FlagConstructor::Opt(opt::cap_lints),
347 "codegen" => FlagConstructor::Opt(opt::codegen),
348 "version" => FlagConstructor::Flag(Flag::Version),
349 "verbose" => FlagConstructor::Flag(Flag::Verbose),
350 "extern" => FlagConstructor::Opt(opt::extern_),
351 "extern-location" => FlagConstructor::Opt(opt::extern_location),
352 "sysroot" => FlagConstructor::Opt(opt::sysroot),
353 "error-format" => FlagConstructor::Opt(opt::error_format),
354 "json" => FlagConstructor::Opt(opt::json),
355 "color" => FlagConstructor::Opt(opt::color),
356 "remap-path-prefix" => FlagConstructor::Opt(opt::remap_path_prefix),
357 _ => FlagConstructor::Unrecognized,
358 }
359}
360
361pub(crate) fn parse(f: &mut RustFlags) -> Option<Flag> {
362 const SEPARATOR: char = '\x1F';
363
364 let mut skip = false;
365
366 while f.pos < f.encoded.len() {
367 if skip {
368 match f.encoded[f.pos..].find(SEPARATOR) {
369 Some(i) => f.pos += i + 1,
371 None => f.pos = f.encoded.len(),
373 }
374 skip = false;
375 continue;
376 }
377
378 let (constructor, arg) = if let Some((constructor, len)) = f.repeat.take() {
379 let arg = &f.encoded[f.pos..f.pos + len];
380 f.pos += len;
381 (ConstructorFn::Repeated(constructor), arg)
382 } else if f.short {
383 let ch = match f.encoded[f.pos..].first_char().unwrap() {
384 EnvChar::Valid(ch) => {
385 f.pos += ch.len_utf8();
386 if ch == SEPARATOR {
387 f.short = false;
388 continue;
389 }
390 ch
391 }
392 EnvChar::Invalid => '\0',
393 };
394 let constructor = match lookup_short(ch) {
395 FlagConstructor::Flag(flag) => return Some(flag),
396 FlagConstructor::Opt(f) => ConstructorFn::Opt(f),
397 FlagConstructor::Repeated(f) => ConstructorFn::Repeated(f),
398 FlagConstructor::Unrecognized => {
399 f.short = false;
400 skip = true;
401 continue;
402 }
403 };
404 f.short = false;
405 if f.pos == f.encoded.len() {
406 break;
407 }
408 if f.encoded[f.pos..].starts_with(SEPARATOR) {
409 f.pos += 1;
411 }
412 let arg = if let Some(i) = f.encoded[f.pos..].find(SEPARATOR) {
413 let arg = &f.encoded[f.pos..f.pos + i];
415 f.pos += i + 1;
416 arg
417 } else {
418 let arg = &f.encoded[f.pos..];
420 f.pos = f.encoded.len();
421 arg
422 };
423 (constructor, arg)
424 } else if f.encoded[f.pos..].starts_with('-') {
425 let Some(first_char) = f.encoded[f.pos + 1..].first_char() else {
426 f.pos += 1;
428 continue;
429 };
430 match first_char {
431 EnvChar::Valid(SEPARATOR) => {
433 f.pos += 2;
434 continue;
435 }
436 EnvChar::Valid('-') => {
437 let flag = match f.encoded[f.pos + 2..].find(SEPARATOR) {
438 Some(0) => {
440 f.pos = f.encoded.len();
441 continue;
442 }
443 Some(i) => {
444 let flag = &f.encoded[f.pos + 2..f.pos + 2 + i];
445 f.pos += i + 3;
446 flag
447 }
448 None => {
449 let flag = &f.encoded[f.pos + 2..];
450 f.pos = f.encoded.len();
451 flag
452 }
453 };
454 let (name, arg) = match flag.split_once('=') {
455 Some((name, arg)) => (name, Some(arg)),
456 None => (flag, None),
457 };
458 let Some(name) = name.to_str() else {
459 continue;
460 };
461 let constructor = match lookup_long(name) {
462 FlagConstructor::Flag(flag) if arg.is_none() => return Some(flag),
464 FlagConstructor::Opt(f) => ConstructorFn::Opt(f),
465 FlagConstructor::Repeated(f) => ConstructorFn::Repeated(f),
466 FlagConstructor::Unrecognized | FlagConstructor::Flag(_) => continue,
467 };
468 let arg = if let Some(arg) = arg {
469 arg
471 } else if let Some(i) = f.encoded[f.pos..].find(SEPARATOR) {
472 let arg = &f.encoded[f.pos..f.pos + i];
474 f.pos += i + 1;
475 arg
476 } else {
477 let arg = &f.encoded[f.pos..];
479 f.pos = f.encoded.len();
480 arg
481 };
482 (constructor, arg)
483 }
484 EnvChar::Valid(_) | EnvChar::Invalid => {
486 f.pos += 1;
487 f.short = true;
488 continue;
489 }
490 }
491 } else {
492 skip = true;
493 continue;
494 };
495
496 enum ConstructorFn {
497 Opt(fn(&EnvStr) -> Option<Flag>),
498 Repeated(fn(&EnvStr) -> Option<(Flag, usize)>),
499 }
500
501 match constructor {
502 ConstructorFn::Opt(constructor) => {
503 if let Some(flag) = constructor(arg) {
504 return Some(flag);
505 }
506 }
507 ConstructorFn::Repeated(constructor) => {
508 if let Some((flag, repeat)) = constructor(arg) {
509 if repeat > 0 {
510 f.pos -= repeat + f.encoded[..f.pos].ends_with(SEPARATOR) as usize;
511 f.repeat = Some((constructor, repeat));
512 }
513 return Some(flag);
514 }
515 }
516 }
517 }
518
519 None
520}