#[cfg(feature = "aur")]
use std::time::Duration;
#[cfg(feature = "aur")]
#[must_use]
pub fn env_timeout() -> Option<Duration> {
std::env::var("ARCH_TOOLKIT_TIMEOUT")
.ok()
.and_then(|v| v.parse::<u64>().ok())
.map(Duration::from_secs)
}
#[cfg(feature = "aur")]
#[must_use]
pub fn env_user_agent() -> Option<String> {
std::env::var("ARCH_TOOLKIT_USER_AGENT")
.ok()
.filter(|s| !s.is_empty())
}
#[cfg(feature = "aur")]
#[must_use]
pub fn env_health_check_timeout() -> Option<Duration> {
std::env::var("ARCH_TOOLKIT_HEALTH_CHECK_TIMEOUT")
.ok()
.and_then(|v| v.parse::<u64>().ok())
.map(Duration::from_secs)
}
#[cfg(feature = "aur")]
#[must_use]
pub fn env_max_retries() -> Option<u32> {
std::env::var("ARCH_TOOLKIT_MAX_RETRIES")
.ok()
.and_then(|v| v.parse::<u32>().ok())
}
#[cfg(feature = "aur")]
#[must_use]
pub fn env_retry_enabled() -> Option<bool> {
std::env::var("ARCH_TOOLKIT_RETRY_ENABLED")
.ok()
.and_then(|v| {
let lower = v.to_lowercase();
match lower.as_str() {
"true" | "1" | "yes" | "on" => Some(true),
"false" | "0" | "no" | "off" => Some(false),
_ => None,
}
})
}
#[cfg(feature = "aur")]
#[must_use]
pub fn env_retry_initial_delay_ms() -> Option<u64> {
std::env::var("ARCH_TOOLKIT_RETRY_INITIAL_DELAY_MS")
.ok()
.and_then(|v| v.parse::<u64>().ok())
}
#[cfg(feature = "aur")]
#[must_use]
pub fn env_retry_max_delay_ms() -> Option<u64> {
std::env::var("ARCH_TOOLKIT_RETRY_MAX_DELAY_MS")
.ok()
.and_then(|v| v.parse::<u64>().ok())
}
#[cfg(feature = "aur")]
#[must_use]
pub fn env_validation_strict() -> Option<bool> {
std::env::var("ARCH_TOOLKIT_VALIDATION_STRICT")
.ok()
.and_then(|v| {
let lower = v.to_lowercase();
match lower.as_str() {
"true" | "1" | "yes" | "on" => Some(true),
"false" | "0" | "no" | "off" => Some(false),
_ => None,
}
})
}
#[cfg(feature = "aur")]
#[must_use]
pub fn env_cache_size() -> Option<usize> {
std::env::var("ARCH_TOOLKIT_CACHE_SIZE")
.ok()
.and_then(|v| v.parse::<usize>().ok())
}
#[cfg(test)]
#[allow(clippy::redundant_pub_crate)]
pub(crate) mod test_support {
use std::sync::{Mutex, MutexGuard, OnceLock, PoisonError};
pub(crate) const MANAGED_VARS: &[&str] = &[
"ARCH_TOOLKIT_TIMEOUT",
"ARCH_TOOLKIT_USER_AGENT",
"ARCH_TOOLKIT_HEALTH_CHECK_TIMEOUT",
"ARCH_TOOLKIT_MAX_RETRIES",
"ARCH_TOOLKIT_RETRY_ENABLED",
"ARCH_TOOLKIT_RETRY_INITIAL_DELAY_MS",
"ARCH_TOOLKIT_RETRY_MAX_DELAY_MS",
"ARCH_TOOLKIT_VALIDATION_STRICT",
"ARCH_TOOLKIT_CACHE_SIZE",
];
static ENV_MUTEX: OnceLock<Mutex<()>> = OnceLock::new();
pub(crate) struct EnvGuard {
_lock: MutexGuard<'static, ()>,
saved: Vec<(&'static str, Option<String>)>,
}
#[allow(clippy::unused_self)]
impl EnvGuard {
pub(crate) fn set(&self, key: &'static str, value: &str) {
assert!(
MANAGED_VARS.contains(&key),
"{key} is not a managed ARCH_TOOLKIT_* test variable"
);
unsafe {
std::env::set_var(key, value);
}
}
pub(crate) fn remove(&self, key: &'static str) {
assert!(
MANAGED_VARS.contains(&key),
"{key} is not a managed ARCH_TOOLKIT_* test variable"
);
unsafe {
std::env::remove_var(key);
}
}
}
impl Drop for EnvGuard {
fn drop(&mut self) {
for (key, value) in &self.saved {
unsafe {
match value {
Some(previous) => std::env::set_var(key, previous),
None => std::env::remove_var(key),
}
}
}
}
}
pub(crate) fn lock_env() -> EnvGuard {
let lock = ENV_MUTEX
.get_or_init(|| Mutex::new(()))
.lock()
.unwrap_or_else(PoisonError::into_inner);
let saved = MANAGED_VARS
.iter()
.map(|key| (*key, std::env::var(key).ok()))
.collect();
for key in MANAGED_VARS {
unsafe {
std::env::remove_var(key);
}
}
EnvGuard { _lock: lock, saved }
}
}
#[cfg(test)]
#[cfg(feature = "aur")]
#[allow(clippy::significant_drop_tightening)]
mod tests {
use super::test_support::lock_env;
use super::*;
#[test]
fn test_env_timeout_valid() {
let env = lock_env();
env.set("ARCH_TOOLKIT_TIMEOUT", "60");
assert_eq!(env_timeout(), Some(Duration::from_mins(1)));
}
#[test]
fn test_env_timeout_invalid() {
let env = lock_env();
env.set("ARCH_TOOLKIT_TIMEOUT", "invalid");
assert_eq!(env_timeout(), None);
}
#[test]
fn test_env_timeout_missing() {
let _env = lock_env();
assert_eq!(env_timeout(), None);
}
#[test]
fn test_env_user_agent_valid() {
let env = lock_env();
env.set("ARCH_TOOLKIT_USER_AGENT", "my-app/1.0");
assert_eq!(env_user_agent(), Some("my-app/1.0".to_string()));
}
#[test]
fn test_env_user_agent_empty() {
let env = lock_env();
env.set("ARCH_TOOLKIT_USER_AGENT", "");
assert_eq!(env_user_agent(), None);
}
#[test]
fn test_env_user_agent_missing() {
let _env = lock_env();
assert_eq!(env_user_agent(), None);
}
#[test]
fn test_env_health_check_timeout_valid() {
let env = lock_env();
env.set("ARCH_TOOLKIT_HEALTH_CHECK_TIMEOUT", "10");
assert_eq!(env_health_check_timeout(), Some(Duration::from_secs(10)));
}
#[test]
fn test_env_health_check_timeout_missing() {
let _env = lock_env();
assert_eq!(env_health_check_timeout(), None);
}
#[test]
fn test_env_max_retries_valid() {
let env = lock_env();
env.set("ARCH_TOOLKIT_MAX_RETRIES", "5");
assert_eq!(env_max_retries(), Some(5));
}
#[test]
fn test_env_max_retries_invalid() {
let env = lock_env();
env.set("ARCH_TOOLKIT_MAX_RETRIES", "invalid");
assert_eq!(env_max_retries(), None);
}
#[test]
fn test_env_max_retries_missing() {
let _env = lock_env();
assert_eq!(env_max_retries(), None);
}
#[test]
fn test_env_retry_enabled_true() {
let env = lock_env();
for value in ["true", "TRUE", "True", "1", "yes", "YES", "on", "ON"] {
env.set("ARCH_TOOLKIT_RETRY_ENABLED", value);
assert_eq!(env_retry_enabled(), Some(true), "Failed for value: {value}");
}
}
#[test]
fn test_env_retry_enabled_false() {
let env = lock_env();
for value in ["false", "FALSE", "False", "0", "no", "NO", "off", "OFF"] {
env.set("ARCH_TOOLKIT_RETRY_ENABLED", value);
assert_eq!(
env_retry_enabled(),
Some(false),
"Failed for value: {value}"
);
}
}
#[test]
fn test_env_retry_enabled_invalid() {
let env = lock_env();
env.set("ARCH_TOOLKIT_RETRY_ENABLED", "maybe");
assert_eq!(env_retry_enabled(), None);
}
#[test]
fn test_env_retry_enabled_missing() {
let _env = lock_env();
assert_eq!(env_retry_enabled(), None);
}
#[test]
fn test_env_retry_initial_delay_ms_valid() {
let env = lock_env();
env.set("ARCH_TOOLKIT_RETRY_INITIAL_DELAY_MS", "2000");
assert_eq!(env_retry_initial_delay_ms(), Some(2000));
}
#[test]
fn test_env_retry_initial_delay_ms_missing() {
let _env = lock_env();
assert_eq!(env_retry_initial_delay_ms(), None);
}
#[test]
fn test_env_retry_max_delay_ms_valid() {
let env = lock_env();
env.set("ARCH_TOOLKIT_RETRY_MAX_DELAY_MS", "60000");
assert_eq!(env_retry_max_delay_ms(), Some(60000));
}
#[test]
fn test_env_retry_max_delay_ms_missing() {
let _env = lock_env();
assert_eq!(env_retry_max_delay_ms(), None);
}
#[test]
fn test_env_validation_strict_true() {
let env = lock_env();
for value in ["true", "TRUE", "1", "yes", "on"] {
env.set("ARCH_TOOLKIT_VALIDATION_STRICT", value);
assert_eq!(
env_validation_strict(),
Some(true),
"Failed for value: {value}"
);
}
}
#[test]
fn test_env_validation_strict_false() {
let env = lock_env();
for value in ["false", "FALSE", "0", "no", "off"] {
env.set("ARCH_TOOLKIT_VALIDATION_STRICT", value);
assert_eq!(
env_validation_strict(),
Some(false),
"Failed for value: {value}"
);
}
}
#[test]
fn test_env_validation_strict_missing() {
let _env = lock_env();
assert_eq!(env_validation_strict(), None);
}
#[test]
fn test_env_cache_size_valid() {
let env = lock_env();
env.set("ARCH_TOOLKIT_CACHE_SIZE", "200");
assert_eq!(env_cache_size(), Some(200));
}
#[test]
fn test_env_cache_size_invalid() {
let env = lock_env();
env.set("ARCH_TOOLKIT_CACHE_SIZE", "invalid");
assert_eq!(env_cache_size(), None);
}
#[test]
fn test_env_cache_size_missing() {
let _env = lock_env();
assert_eq!(env_cache_size(), None);
}
#[test]
fn test_env_guard_restores_previous_values() {
let outer = lock_env();
outer.set("ARCH_TOOLKIT_TIMEOUT", "111");
{
let inner_saved = std::env::var("ARCH_TOOLKIT_TIMEOUT").ok();
assert_eq!(inner_saved.as_deref(), Some("111"));
}
outer.remove("ARCH_TOOLKIT_TIMEOUT");
assert_eq!(env_timeout(), None);
}
}