pub use temp_env_vars_macro::temp_env_vars;
use std::{
collections::HashMap,
sync::{Arc, LazyLock, Mutex},
};
#[doc(hidden)]
pub static TEMP_ENV_VAR_MACRO_MUTEX: LazyLock<Arc<Mutex<()>>> = LazyLock::new(Arc::default);
#[derive(Debug)]
pub struct TempEnvScope {
original_vars: HashMap<String, String>,
}
impl TempEnvScope {
pub fn new() -> TempEnvScope {
TempEnvScope {
original_vars: std::env::vars().collect(),
}
}
fn restore(&self) {
let mut now: HashMap<String, String> = std::env::vars().collect();
self.original_vars.keys().for_each(|key| {
now.remove(key);
});
now.keys().for_each(|key| {
std::env::remove_var(key);
});
self.original_vars.iter().for_each(|(k, v)| {
std::env::set_var(k, v);
});
}
}
impl Drop for TempEnvScope {
fn drop(&mut self) {
self.restore();
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use assertor::{assert_that, EqualityAssertion, ResultAssertion};
use serial_test::serial;
use super::TempEnvScope;
#[test]
#[serial]
fn test_nothing_is_changed() {
let original: HashMap<String, String> = std::env::vars().collect();
{
let _env_scope = TempEnvScope::new();
}
let after: HashMap<String, String> = std::env::vars().collect();
assert_that!(after).is_equal_to(original);
}
#[test]
#[serial]
fn test_new_vars_are_removed() {
std::env::remove_var("FOO");
let original: HashMap<String, String> = std::env::vars().collect();
{
let _env_scope = TempEnvScope::new();
std::env::set_var("FOO", "BAR1");
}
let after: HashMap<String, String> = std::env::vars().collect();
assert_that!(std::env::var("FOO")).is_err();
assert_that!(after).is_equal_to(original);
}
#[test]
#[serial]
fn test_changed_vars_are_reset() {
std::env::set_var("FOO", "BAR2");
let original: HashMap<String, String> = std::env::vars().collect();
{
let _env_scope = TempEnvScope::new();
std::env::set_var("FOO", "123");
}
let after: HashMap<String, String> = std::env::vars().collect();
assert_that!(std::env::var("FOO")).has_ok("BAR2".to_string());
assert_that!(after).is_equal_to(original);
}
#[test]
#[serial]
fn test_env_vars_are_restored() {
std::env::set_var("FOO", "BAR3");
let original: HashMap<String, String> = std::env::vars().collect();
{
let _env_scope = TempEnvScope::new();
std::env::remove_var("FOO");
}
let after: HashMap<String, String> = std::env::vars().collect();
assert_that!(std::env::var("FOO")).has_ok("BAR3".to_string());
assert_that!(after).is_equal_to(original);
}
#[test]
#[serial]
fn test_two_scopes_active_at_same_time() {
std::env::remove_var("FOO");
{
let _env_scope_1 = TempEnvScope::new();
let _env_scope_2 = TempEnvScope::new();
std::env::set_var("FOO", "BAR4");
assert_that!(std::env::var("FOO")).is_ok();
}
assert_that!(std::env::var("FOO")).is_err();
}
#[test]
#[serial]
fn test_sequential_test_scopes() {
std::env::remove_var("FOO");
{
let _env_scope = TempEnvScope::new();
std::env::set_var("FOO", "BAR5");
assert_that!(std::env::var("FOO")).is_ok();
}
assert_that!(std::env::var("FOO")).is_err();
{
let _env_scope = TempEnvScope::new();
std::env::set_var("FOO", "BAR6");
assert_that!(std::env::var("FOO")).is_ok();
}
assert_that!(std::env::var("FOO")).is_err();
}
}