use oxdock_fs::GuardedPath;
use std::sync::{Mutex, MutexGuard};
static ENV_LOCK: Mutex<()> = Mutex::new(());
pub struct SerialCargoEnv<'a> {
_lock: MutexGuard<'a, ()>,
prev_manifest: Option<String>,
prev_primary: Option<String>,
}
impl<'a> SerialCargoEnv<'a> {
pub fn new(manifest_dir: &GuardedPath, primary: bool) -> Self {
let lock = ENV_LOCK
.lock()
.unwrap_or_else(|poisoned| poisoned.into_inner());
let prev_manifest = std::env::var("CARGO_MANIFEST_DIR").ok();
let prev_primary = std::env::var("CARGO_PRIMARY_PACKAGE").ok();
unsafe {
std::env::set_var("CARGO_MANIFEST_DIR", manifest_dir.as_path());
std::env::set_var("CARGO_PRIMARY_PACKAGE", if primary { "1" } else { "0" });
}
Self {
_lock: lock,
prev_manifest,
prev_primary,
}
}
}
impl Drop for SerialCargoEnv<'_> {
fn drop(&mut self) {
unsafe {
if let Some(prev) = &self.prev_manifest {
std::env::set_var("CARGO_MANIFEST_DIR", prev);
} else {
std::env::remove_var("CARGO_MANIFEST_DIR");
}
if let Some(prev) = &self.prev_primary {
std::env::set_var("CARGO_PRIMARY_PACKAGE", prev);
} else {
std::env::remove_var("CARGO_PRIMARY_PACKAGE");
}
}
}
}
pub fn manifest_env_guard<'a>(manifest_dir: &'a GuardedPath, primary: bool) -> SerialCargoEnv<'a> {
SerialCargoEnv::new(manifest_dir, primary)
}
#[cfg(test)]
impl SerialCargoEnv<'_> {
pub fn previous(&self) -> (Option<String>, Option<String>) {
(self.prev_manifest.clone(), self.prev_primary.clone())
}
}
#[cfg(test)]
mod tests {
use super::manifest_env_guard;
use oxdock_fs::GuardedPath;
#[test]
fn guard_sets_manifest_dir_and_primary_flag() {
let temp = GuardedPath::tempdir().expect("tempdir");
let root = temp.as_guarded_path().clone();
{
let _guard = manifest_env_guard(&root, true);
let expected = root.as_path().to_string_lossy().into_owned();
assert_eq!(
std::env::var("CARGO_MANIFEST_DIR").as_deref(),
Ok(expected.as_str())
);
assert_eq!(std::env::var("CARGO_PRIMARY_PACKAGE").as_deref(), Ok("1"));
}
{
let _guard = manifest_env_guard(&root, false);
assert_eq!(std::env::var("CARGO_PRIMARY_PACKAGE").as_deref(), Ok("0"));
}
}
#[test]
fn guard_captures_and_restores_prior_state() {
let temp_a = GuardedPath::tempdir().expect("tempdir");
let temp_b = GuardedPath::tempdir().expect("tempdir");
let root_a = temp_a.as_guarded_path().clone();
let root_b = temp_b.as_guarded_path().clone();
let expected = {
let probe = manifest_env_guard(&root_a, true);
probe.previous()
};
{
let guard = manifest_env_guard(&root_b, false);
assert_eq!(guard.previous(), expected);
let expected_dir = root_b.as_path().to_string_lossy().into_owned();
assert_eq!(
std::env::var("CARGO_MANIFEST_DIR").as_deref(),
Ok(expected_dir.as_str())
);
}
}
}