magi-code 0.63.1

Repository-aware CLI coding agent for terminal work
Documentation
use std::{
    ffi::{OsStr, OsString},
    sync::{Mutex, MutexGuard, OnceLock},
};

static ENV_LOCK: OnceLock<Mutex<()>> = OnceLock::new();

pub(crate) struct EnvGuard {
    _guard: MutexGuard<'static, ()>,
}

impl EnvGuard {
    pub(crate) fn set_var<K: AsRef<OsStr>, V: AsRef<OsStr>>(&self, key: K, value: V) {
        // SAFETY: EnvGuard holds the global test environment mutex while mutating process env.
        unsafe { std::env::set_var(key, value) };
    }

    pub(crate) fn remove_var<K: AsRef<OsStr>>(&self, key: K) {
        // SAFETY: EnvGuard holds the global test environment mutex while mutating process env.
        unsafe { std::env::remove_var(key) };
    }

    pub(crate) fn save(&self, name: &'static str) -> SavedEnvVar<'_> {
        SavedEnvVar {
            name,
            value: std::env::var_os(name),
            guard: self,
        }
    }
}

pub(crate) fn env_lock() -> EnvGuard {
    EnvGuard {
        _guard: ENV_LOCK.get_or_init(|| Mutex::new(())).lock().unwrap(),
    }
}

pub(crate) struct SavedEnvVar<'a> {
    name: &'static str,
    value: Option<OsString>,
    guard: &'a EnvGuard,
}

impl Drop for SavedEnvVar<'_> {
    fn drop(&mut self) {
        match self.value.as_ref() {
            Some(value) => self.guard.set_var(self.name, value),
            None => self.guard.remove_var(self.name),
        }
    }
}

static CURRENT_DIR_LOCK: OnceLock<Mutex<()>> = OnceLock::new();

pub(crate) struct CurrentDirGuard {
    original: std::path::PathBuf,
    _guard: MutexGuard<'static, ()>,
    restored: bool,
}

impl CurrentDirGuard {
    pub(crate) fn capture() -> Self {
        let guard = CURRENT_DIR_LOCK
            .get_or_init(|| Mutex::new(()))
            .lock()
            .unwrap();
        Self {
            original: std::env::current_dir().unwrap(),
            _guard: guard,
            restored: false,
        }
    }

    pub(crate) fn set_current_dir(&self, path: impl AsRef<std::path::Path>) -> std::io::Result<()> {
        std::env::set_current_dir(path)
    }

    pub(crate) fn restore(&mut self) -> std::io::Result<()> {
        let result = std::env::set_current_dir(&self.original);
        if result.is_ok() {
            self.restored = true;
        }
        result
    }
}

impl Drop for CurrentDirGuard {
    fn drop(&mut self) {
        if !self.restored {
            let _ = std::env::set_current_dir(&self.original);
        }
    }
}