use std::collections::HashSet;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fmt::{Debug, Display, Formatter};
use std::ops::{Deref, DerefMut};
use std::process::Command;
pub struct CargoCmd {
cargo: Command,
vars_os: Vec<(OsString, OsString)>,
removed: HashSet<OsString>,
}
impl CargoCmd {
pub fn new() -> Self {
let mut cargo = CargoCmd::new_no_filtering();
cargo.retain_vars_os(|(key, _)| {
!key.to_str()
.is_some_and(|s| s.starts_with("CARGO_FEATURES_") || s.starts_with("CARGO_CFG_"))
});
cargo.env("CARGO_CACHE_RUSTC_INFO", "0");
cargo.env_remove("RUSTC");
cargo.env_remove("RUSTC_WRAPPER");
cargo
.env_remove("RUSTC_WORKSPACE_WRAPPER")
.env_remove("RUSTFLAGS")
.env_remove("CARGO")
.env_remove("RUSTUP_TOOLCHAIN");
cargo.env_remove("CARGO_ENCODED_RUSTFLAGS");
cargo.env_remove("CARGO_TARGET_DIR");
cargo
}
pub fn new_no_filtering() -> Self {
Self {
cargo: Command::new("cargo"),
vars_os: env::vars_os().collect(),
removed: HashSet::default(),
}
}
pub fn retain_vars_os(&mut self, mut f: impl FnMut((&OsString, &OsString)) -> bool) {
for (key, value) in &self.vars_os {
if !f((key, value)) {
self.removed.insert(key.clone());
self.cargo.env_remove(key);
}
}
}
pub fn env_remove(&mut self, key: impl AsRef<OsStr>) -> &mut Self {
self.removed.insert(key.as_ref().to_os_string());
self.cargo.env_remove(key);
self
}
pub fn env(&mut self, key: impl AsRef<OsStr>, val: impl AsRef<OsStr>) -> &mut Self {
self.removed.remove(key.as_ref());
self.cargo.env(key, val);
self
}
pub fn env_var_report(&self) -> EnvVarReport {
let mut inherited = self.vars_os.clone();
inherited.retain(|(key, _)| !self.removed.contains(key));
EnvVarReport {
inherited,
removed: self.removed.clone(),
}
}
}
impl Default for CargoCmd {
fn default() -> Self {
Self::new()
}
}
impl Debug for CargoCmd {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("CargoCmd")
.field("cargo", &self.cargo)
.field("env_vars", &self.env_var_report())
.finish()
}
}
impl From<CargoCmd> for Command {
fn from(cmd: CargoCmd) -> Self {
cmd.cargo
}
}
impl Deref for CargoCmd {
type Target = Command;
fn deref(&self) -> &Self::Target {
&self.cargo
}
}
impl DerefMut for CargoCmd {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cargo
}
}
#[derive(Clone, Debug, Default)]
pub struct EnvVarReport {
pub inherited: Vec<(OsString, OsString)>,
pub removed: HashSet<OsString>,
}
impl Display for EnvVarReport {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let removed = self
.removed
.iter()
.map(|key| format!("{}", key.to_string_lossy()))
.collect::<Vec<_>>();
let inherited = self
.inherited
.iter()
.map(|(key, value)| format!("{}: {}", key.to_string_lossy(), value.to_string_lossy()))
.collect::<Vec<_>>();
f.debug_struct("EnvVarReport")
.field("removed", &removed)
.field("inherited", &inherited)
.finish()
}
}