use crate::{
constants::{CARGO_DEBUG, CARGO_FEATURES, CARGO_OPT_LEVEL, CARGO_TARGET_TRIPLE},
emitter::{EmitBuilder, RustcEnvMap},
key::VergenKey,
utils::fns::{add_default_map_entry, add_map_entry},
};
use anyhow::{Error, Result};
use std::env;
#[derive(Clone, Copy, Debug, Default)]
#[allow(clippy::struct_excessive_bools)]
pub(crate) struct Config {
pub(crate) cargo_debug: bool,
pub(crate) cargo_features: bool,
pub(crate) cargo_opt_level: bool,
pub(crate) cargo_target_triple: bool,
}
#[cfg_attr(docsrs, doc(cfg(feature = "cargo")))]
impl EmitBuilder {
pub fn all_cargo(&mut self) -> &mut Self {
self.cargo_debug()
.cargo_features()
.cargo_opt_level()
.cargo_target_triple()
}
pub fn cargo_debug(&mut self) -> &mut Self {
self.cargo_config.cargo_debug = true;
self
}
pub fn cargo_features(&mut self) -> &mut Self {
self.cargo_config.cargo_features = true;
self
}
pub fn cargo_opt_level(&mut self) -> &mut Self {
self.cargo_config.cargo_opt_level = true;
self
}
pub fn cargo_target_triple(&mut self) -> &mut Self {
self.cargo_config.cargo_target_triple = true;
self
}
pub(crate) fn add_cargo_default(
&self,
e: Error,
fail_on_error: bool,
map: &mut RustcEnvMap,
warnings: &mut Vec<String>,
) -> Result<()> {
println!("{e:?}");
if fail_on_error {
Err(e)
} else {
if self.cargo_config.cargo_debug {
add_default_map_entry(VergenKey::CargoDebug, map, warnings);
}
if self.cargo_config.cargo_features {
add_default_map_entry(VergenKey::CargoFeatures, map, warnings);
}
if self.cargo_config.cargo_opt_level {
add_default_map_entry(VergenKey::CargoOptLevel, map, warnings);
}
if self.cargo_config.cargo_target_triple {
add_default_map_entry(VergenKey::CargoTargetTriple, map, warnings);
}
Ok(())
}
}
pub(crate) fn add_cargo_map_entries(&self, map: &mut RustcEnvMap) -> Result<()> {
if self.cargo_config.cargo_debug {
if let Ok(value) = env::var(CARGO_DEBUG) {
add_map_entry(VergenKey::CargoDebug, value, map);
} else {
add_map_entry(VergenKey::CargoDebug, env::var("DEBUG")?, map);
}
}
if self.cargo_config.cargo_features {
if let Ok(value) = env::var(CARGO_FEATURES) {
add_map_entry(VergenKey::CargoFeatures, value, map);
} else {
let features: Vec<String> = env::vars().filter_map(is_cargo_feature).collect();
let feature_str = features.as_slice().join(",");
add_map_entry(VergenKey::CargoFeatures, feature_str, map);
}
}
if self.cargo_config.cargo_opt_level {
if let Ok(value) = env::var(CARGO_OPT_LEVEL) {
add_map_entry(VergenKey::CargoOptLevel, value, map);
} else {
add_map_entry(VergenKey::CargoOptLevel, env::var("OPT_LEVEL")?, map);
}
}
if self.cargo_config.cargo_target_triple {
if let Ok(value) = env::var(CARGO_TARGET_TRIPLE) {
add_map_entry(VergenKey::CargoTargetTriple, value, map);
} else {
add_map_entry(VergenKey::CargoTargetTriple, env::var("TARGET")?, map);
}
}
Ok(())
}
}
fn is_cargo_feature(var: (String, String)) -> Option<String> {
let (k, _) = var;
if k.starts_with("CARGO_FEATURE_") {
Some(k.replace("CARGO_FEATURE_", "").to_lowercase())
} else {
None
}
}
#[cfg(test)]
mod test {
use crate::{
emitter::test::count_idempotent,
utils::testutils::{setup, teardown},
EmitBuilder,
};
use anyhow::Result;
use std::env;
#[test]
#[serial_test::serial]
fn build_all_idempotent() -> Result<()> {
setup();
let config = EmitBuilder::builder()
.idempotent()
.all_cargo()
.test_emit()?;
assert_eq!(4, config.cargo_rustc_env_map.len());
assert_eq!(0, count_idempotent(&config.cargo_rustc_env_map));
assert_eq!(0, config.warnings.len());
teardown();
Ok(())
}
#[test]
#[serial_test::serial]
fn build_all() -> Result<()> {
setup();
let config = EmitBuilder::builder().all_cargo().test_emit()?;
assert_eq!(4, config.cargo_rustc_env_map.len());
assert_eq!(0, count_idempotent(&config.cargo_rustc_env_map));
assert_eq!(0, config.warnings.len());
teardown();
Ok(())
}
#[test]
#[serial_test::serial]
fn bad_env_fails() {
assert!(EmitBuilder::builder()
.fail_on_error()
.all_cargo()
.test_emit()
.is_err());
}
#[test]
#[serial_test::serial]
fn bad_env_emits_default() -> Result<()> {
let emit_res = EmitBuilder::builder().all_cargo().test_emit();
assert!(emit_res.is_ok());
let emit = emit_res?;
assert_eq!(4, emit.cargo_rustc_env_map.len());
assert_eq!(4, count_idempotent(&emit.cargo_rustc_env_map));
assert_eq!(4, emit.warnings.len());
Ok(())
}
#[test]
#[serial_test::serial]
fn cargo_debug_override_works() -> Result<()> {
setup();
env::set_var("VERGEN_CARGO_DEBUG", "this is a bad date");
let mut stdout_buf = vec![];
assert!(EmitBuilder::builder()
.all_cargo()
.emit_to(&mut stdout_buf)
.is_ok());
let output = String::from_utf8_lossy(&stdout_buf);
assert!(output.contains("cargo:rustc-env=VERGEN_CARGO_DEBUG=this is a bad date"));
env::remove_var("VERGEN_CARGO_DEBUG");
teardown();
Ok(())
}
#[test]
#[serial_test::serial]
fn cargo_features_override_works() -> Result<()> {
setup();
env::set_var("VERGEN_CARGO_FEATURES", "this is a bad date");
let mut stdout_buf = vec![];
assert!(EmitBuilder::builder()
.all_cargo()
.emit_to(&mut stdout_buf)
.is_ok());
let output = String::from_utf8_lossy(&stdout_buf);
println!("{output}");
assert!(output.contains("cargo:rustc-env=VERGEN_CARGO_FEATURES=this is a bad date"));
env::remove_var("VERGEN_CARGO_FEATURES");
teardown();
Ok(())
}
#[test]
#[serial_test::serial]
fn cargo_opt_level_override_works() -> Result<()> {
setup();
env::set_var("VERGEN_CARGO_OPT_LEVEL", "this is a bad date");
let mut stdout_buf = vec![];
assert!(EmitBuilder::builder()
.all_cargo()
.emit_to(&mut stdout_buf)
.is_ok());
let output = String::from_utf8_lossy(&stdout_buf);
assert!(output.contains("cargo:rustc-env=VERGEN_CARGO_OPT_LEVEL=this is a bad date"));
env::remove_var("VERGEN_CARGO_OPT_LEVEL");
teardown();
Ok(())
}
#[test]
#[serial_test::serial]
fn cargo_target_triple_override_works() -> Result<()> {
setup();
env::set_var("VERGEN_CARGO_TARGET_TRIPLE", "this is a bad date");
let mut stdout_buf = vec![];
assert!(EmitBuilder::builder()
.all_cargo()
.emit_to(&mut stdout_buf)
.is_ok());
let output = String::from_utf8_lossy(&stdout_buf);
assert!(output.contains("cargo:rustc-env=VERGEN_CARGO_TARGET_TRIPLE=this is a bad date"));
env::remove_var("VERGEN_CARGO_TARGET_TRIPLE");
teardown();
Ok(())
}
}