//! Test-only utilities for safely mutating process-global state in tests.

/// RAII guard for temporarily setting an environment variable.
///
/// The variable is automatically restored to its previous state (or removed if it
/// was not set) when the guard is dropped.
pub struct EnvGuard {
    key: &'static str,
    prev: Option<String>,
}

impl EnvGuard {
    /// Set an environment variable temporarily.
    ///
    /// # Safety
    ///
    /// This function uses `unsafe` because `std::env::set_var` can cause data races
    /// if called concurrently. Safe when used with `#[serial(env)]`.
    #[must_use]
    pub fn set(key: &'static str, val: &str) -> Self {
        let prev = std::env::var(key).ok();
        // SAFETY: Callers use #[serial(env)] to ensure no concurrent environment access.
        unsafe { std::env::set_var(key, val) };
        Self { key, prev }
    }

    /// Remove an environment variable temporarily.
    ///
    /// # Safety
    ///
    /// This function uses `unsafe` because `std::env::remove_var` can cause data races
    /// if called concurrently. Safe when used with `#[serial(env)]`.
    #[must_use]
    pub fn remove(key: &'static str) -> Self {
        let prev = std::env::var(key).ok();
        // SAFETY: Callers use #[serial(env)] to ensure no concurrent environment access.
        unsafe { std::env::remove_var(key) };
        Self { key, prev }
    }
}

impl Drop for EnvGuard {
    fn drop(&mut self) {
        match &self.prev {
            Some(v) => {
                // SAFETY: Callers use #[serial(env)] to ensure no concurrent environment access.
                unsafe { std::env::set_var(self.key, v) }
            }
            None => {
                // SAFETY: Callers use #[serial(env)] to ensure no concurrent environment access.
                unsafe { std::env::remove_var(self.key) }
            }
        }
    }
}
