cargo-worktree 1.0.0

Deterministic build namespace layer for Cargo
use crate::runtime::env::EnvMap;
use anyhow::{Context, Result};
use std::path::PathBuf;
use std::process::Command;

pub fn exec_cargo(subcommand: &str, args: &[String], extra_env: &EnvMap) -> Result<()> {
    let mut cmd = Command::new("cargo");
    cmd.arg(subcommand).args(args);

    if let Some(cargo_path) = resolve_cargo_path() {
        cmd.env("CARGO", cargo_path);
    }

    for (k, _) in std::env::vars_os() {
        #[allow(clippy::collapsible_if)]
        if let Some(key) = k.to_str() {
            if key.starts_with("CARGO_") || key == "CARGO" || key == "MAKEFLAGS" || key == "MFLAGS"
            {
                cmd.env_remove(key);
            }
        }
    }

    for (k, v) in extra_env {
        cmd.env(k, v);
    }

    cmd.stdin(std::process::Stdio::inherit());
    cmd.stdout(std::process::Stdio::inherit());
    cmd.stderr(std::process::Stdio::inherit());

    let status = cmd.status().context("failed to spawn cargo")?;
    std::process::exit(status.code().unwrap_or(1));
}

fn resolve_cargo_path() -> Option<PathBuf> {
    if let Some(path) = std::env::var_os("CARGO") {
        let path = PathBuf::from(path);
        if path.is_file() {
            return Some(path);
        }
    }

    which::which("cargo").ok()
}