pub fn translate_cargo_to_rustc(option: &str) -> Option<&'static str> {
match option {
"--release" => Some("-O"),
"--debug" => Some("-g"),
"--target" => Some("--target"), "--verbose" | "-v" => Some("-v"),
"--quiet" | "-q" => None, "--features" => None, "--all-features" => None, "--no-default-features" => None, "--bin" => None, "--lib" => Some("--crate-type=lib"),
"--example" => None, "--test" => Some("--test"),
"--bench" => None, _ => None,
}
}
pub fn is_valid_rustc_option(option: &str) -> bool {
if option.len() == 2 && option.starts_with('-') {
matches!(
option,
"-h" | "-g"
| "-O"
| "-o"
| "-A"
| "-W"
| "-D"
| "-F"
| "-C"
| "-V"
| "-v"
| "-L"
| "-l"
)
} else if option.starts_with("--") {
matches!(
option,
"--help"
| "--cfg"
| "--check-cfg"
| "--crate-type"
| "--crate-name"
| "--edition"
| "--emit"
| "--print"
| "--out-dir"
| "--explain"
| "--test"
| "--target"
| "--allow"
| "--warn"
| "--force-warn"
| "--deny"
| "--forbid"
| "--cap-lints"
| "--codegen"
| "--version"
| "--verbose"
| "--extern"
| "--sysroot"
| "--error-format"
| "--json"
| "--color"
| "--diagnostic-width"
| "--remap-path-prefix"
| "--env-set"
)
} else if option.starts_with("-C") || option.starts_with("--codegen") {
true
} else if option.starts_with("-L") || option.starts_with("-l") {
true
} else {
false
}
}
pub fn get_rustc_optimization_flags(profile: &str) -> Vec<&'static str> {
match profile {
"release" => vec!["-O", "-C", "lto=yes", "-C", "codegen-units=1"],
"debug" => vec!["-g", "-C", "opt-level=0"],
"test" => vec!["--test", "-g"],
_ => vec![],
}
}
pub fn get_rustc_translation_help() -> &'static str {
r#"Cargo to Rustc Option Translation:
Common Cargo options and their Rustc equivalents:
--release → -O (optimize) + -C lto=yes + -C codegen-units=1
--debug → -g (debug info)
--test → --test (build test harness)
--lib → --crate-type=lib
--target <TARGET> → --target <TARGET>
--verbose/-v → -v
Options with no Rustc equivalent (will be ignored):
--features, --all-features, --no-default-features
--bin, --example, --bench
--quiet/-q
For rustc scripts, you can use native rustc options:
-O Optimize (equivalent to -C opt-level=3)
-g Include debug info
-C <OPT>=<VAL> Set codegen options (e.g., -C lto=yes)
--edition Set Rust edition (2015|2018|2021|2024)
--crate-type Set output type (bin|lib|rlib|dylib|cdylib|staticlib)
Example overrides for rustc:
-O # Optimize for release
-g # Include debug symbols
--edition 2021 # Use Rust 2021 edition
-C target-cpu=native # Optimize for native CPU
"#
}
pub fn validate_and_translate_options(options: &[String], is_rustc: bool) -> Vec<String> {
if !is_rustc {
return options.to_vec();
}
let mut translated = Vec::new();
let mut skip_next = false;
for (i, option) in options.iter().enumerate() {
if skip_next {
skip_next = false;
continue;
}
if let Some(rustc_opt) = translate_cargo_to_rustc(option) {
translated.push(rustc_opt.to_string());
if matches!(option.as_str(), "--target") && i + 1 < options.len() {
translated.push(options[i + 1].clone());
skip_next = true;
}
} else if is_valid_rustc_option(option) {
translated.push(option.clone());
if needs_value(option) && i + 1 < options.len() && !options[i + 1].starts_with('-') {
translated.push(options[i + 1].clone());
skip_next = true;
}
} else if option == "--profile" && i + 1 < options.len() {
let profile = &options[i + 1];
translated.extend(
get_rustc_optimization_flags(profile)
.iter()
.map(|s| s.to_string()),
);
skip_next = true;
}
}
translated
}
fn needs_value(option: &str) -> bool {
matches!(
option,
"--cfg"
| "--check-cfg"
| "--crate-type"
| "--crate-name"
| "--edition"
| "--emit"
| "--print"
| "--out-dir"
| "--explain"
| "--target"
| "--allow"
| "--warn"
| "--force-warn"
| "--deny"
| "--forbid"
| "--cap-lints"
| "--codegen"
| "-o"
| "-L"
| "-l"
| "--extern"
| "--sysroot"
| "--error-format"
| "--json"
| "--color"
| "--diagnostic-width"
| "--remap-path-prefix"
| "--env-set"
) || option == "-C"
|| option == "-A"
|| option == "-W"
|| option == "-D"
|| option == "-F"
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_translate_cargo_to_rustc() {
assert_eq!(translate_cargo_to_rustc("--release"), Some("-O"));
assert_eq!(translate_cargo_to_rustc("--debug"), Some("-g"));
assert_eq!(translate_cargo_to_rustc("--test"), Some("--test"));
assert_eq!(translate_cargo_to_rustc("--lib"), Some("--crate-type=lib"));
assert_eq!(translate_cargo_to_rustc("--features"), None);
}
#[test]
fn test_is_valid_rustc_option() {
assert!(is_valid_rustc_option("-O"));
assert!(is_valid_rustc_option("-g"));
assert!(is_valid_rustc_option("--test"));
assert!(is_valid_rustc_option("--target"));
assert!(is_valid_rustc_option("-C"));
assert!(!is_valid_rustc_option("--release"));
assert!(!is_valid_rustc_option("--features"));
}
#[test]
fn test_validate_and_translate_options() {
let options = vec![
"--release".to_string(),
"--target".to_string(),
"wasm32-unknown-unknown".to_string(),
"--features".to_string(),
"foo,bar".to_string(),
];
let translated = validate_and_translate_options(&options, true);
assert_eq!(
translated,
vec![
"-O".to_string(),
"--target".to_string(),
"wasm32-unknown-unknown".to_string(),
]
);
}
}