malvin 0.2.5

Non-interactive research and coding agent
use std::path::{Path, PathBuf};

struct IsolatedTestEnv {
    _tmp: tempfile::TempDir,
    old_home: Option<std::ffi::OsString>,
    old_home_config_mutation: Option<std::ffi::OsString>,
    old_cwd: PathBuf,
}

fn stable_fallback_cwd() -> PathBuf {
    PathBuf::from(env!("CARGO_MANIFEST_DIR"))
}

impl IsolatedTestEnv {
    fn new() -> (Self, PathBuf) {
        let tmp = tempfile::tempdir().unwrap();
        let home = tmp.path().join("home");
        std::fs::create_dir_all(&home).unwrap();
        let old_home = std::env::var_os("HOME");
        let old_home_config_mutation =
            std::env::var_os(crate::MALVIN_TEST_ALLOW_HOME_CONFIG_MUTATION);
        {
            super::env::set_test_home_env(&home);
        }
        let work = tmp.path().join("repo");
        std::fs::create_dir_all(&work).unwrap();
        let old_cwd = std::env::current_dir().unwrap_or_else(|_| {
            let stable = stable_fallback_cwd();
            std::env::set_current_dir(&stable).expect("chdir stable fallback");
            stable
        });
        std::env::set_current_dir(&work).expect("chdir isolated workspace");
        (
            Self {
                _tmp: tmp,
                old_home,
                old_home_config_mutation,
                old_cwd,
            },
            work,
        )
    }
}

impl Drop for IsolatedTestEnv {
    fn drop(&mut self) {
        if std::env::set_current_dir(&self.old_cwd).is_err() {
            let _ = std::env::set_current_dir(stable_fallback_cwd());
        }
        #[allow(unsafe_code)]
        unsafe {
            match self.old_home.take() {
                Some(h) => std::env::set_var("HOME", h),
                None => std::env::remove_var("HOME"),
            }
            match self.old_home_config_mutation.take() {
                Some(v) => std::env::set_var(crate::MALVIN_TEST_ALLOW_HOME_CONFIG_MUTATION, v),
                None => std::env::remove_var(crate::MALVIN_TEST_ALLOW_HOME_CONFIG_MUTATION),
            }
        }
    }
}

pub fn with_isolated_home<F>(f: F)
where
    F: FnOnce(&Path),
{
    let _lock = super::test_env_lock();
    let (_env, work) = IsolatedTestEnv::new();
    f(&work);
}