use std::collections::HashMap;
use std::ffi::OsString;
use std::str::FromStr;
use std::sync::{OnceLock, RwLock};
static OVERRIDES: OnceLock<RwLock<HashMap<String, String>>> = OnceLock::new();
fn map() -> &'static RwLock<HashMap<String, String>> {
OVERRIDES.get_or_init(|| RwLock::new(HashMap::new()))
}
pub fn normalize_key(key: &str) -> String {
if key.starts_with("RLX_") {
key.to_string()
} else {
format!("RLX_{key}")
}
}
pub fn set(key: impl AsRef<str>, value: impl Into<String>) {
let key = normalize_key(key.as_ref());
if let Ok(mut g) = map().write() {
g.insert(key, value.into());
}
}
pub fn unset(key: impl AsRef<str>) {
let key = normalize_key(key.as_ref());
if let Ok(mut g) = map().write() {
g.remove(&key);
}
}
pub fn clear_overrides() {
if let Ok(mut g) = map().write() {
g.clear();
}
}
pub fn var(key: &str) -> Option<String> {
let key = normalize_key(key);
if let Ok(g) = map().read() {
if let Some(v) = g.get(&key) {
return Some(v.clone());
}
}
std::env::var(&key).ok()
}
pub fn var_os(key: &str) -> Option<OsString> {
var(key).map(Into::into)
}
pub fn flag(key: &str) -> bool {
match var(key) {
Some(v) => truthy(&v),
None => false,
}
}
pub fn is_unset(key: &str) -> bool {
var(key).is_none()
}
pub fn parse_or<T: FromStr>(key: &str, default: T) -> T {
var(key).and_then(|s| s.parse().ok()).unwrap_or(default)
}
fn truthy(v: &str) -> bool {
let s = v.trim();
if s.is_empty() {
return false;
}
match s.to_ascii_lowercase().as_str() {
"0" | "false" | "off" | "no" => false,
"1" | "true" | "yes" | "on" => true,
_ if s.chars().all(|c| c.is_ascii_digit()) => s != "0",
_ => true, }
}
#[derive(Debug, Clone, Default)]
pub struct RlxEnv {
pairs: Vec<(String, String)>,
}
impl RlxEnv {
pub fn new() -> Self {
Self::default()
}
pub fn set(mut self, key: impl AsRef<str>, value: impl Into<String>) -> Self {
self.pairs.push((normalize_key(key.as_ref()), value.into()));
self
}
pub fn flag(mut self, key: impl AsRef<str>, on: bool) -> Self {
self.pairs.push((
normalize_key(key.as_ref()),
if on { "1" } else { "0" }.into(),
));
self
}
pub fn apply(self) {
for (k, v) in self.pairs {
set(&k, v);
}
}
}
pub struct RuntimeOverrides {
saved: Vec<(String, Option<String>)>,
}
impl RuntimeOverrides {
pub fn install(pairs: impl IntoIterator<Item = (impl AsRef<str>, impl Into<String>)>) -> Self {
let mut saved = Vec::new();
for (key, value) in pairs {
let key = normalize_key(key.as_ref());
let prev = map().read().ok().and_then(|g| g.get(&key).cloned());
saved.push((key.clone(), prev));
set(&key, value);
}
Self { saved }
}
}
impl Drop for RuntimeOverrides {
fn drop(&mut self) {
for (key, prev) in self.saved.drain(..) {
match prev {
Some(v) => set(&key, v),
None => unset(&key),
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::sync::Mutex;
static ENV_TEST_LOCK: Mutex<()> = Mutex::new(());
fn with_clean_overrides(f: impl FnOnce()) {
let _guard = ENV_TEST_LOCK.lock().expect("env test lock poisoned");
clear_overrides();
f();
clear_overrides();
}
#[test]
fn code_override_wins_over_process_env() {
with_clean_overrides(|| {
let _g = RuntimeOverrides::install([("VERBOSE", "2")]);
assert_eq!(var("RLX_VERBOSE"), Some("2".into()));
assert!(flag("RLX_VERBOSE"));
});
}
#[test]
fn flag_parses_falsy_override() {
with_clean_overrides(|| {
set("RLX_DISABLE_MPSGRAPH", "0");
assert!(!flag("RLX_DISABLE_MPSGRAPH"));
});
}
#[test]
fn rlx_env_bulk_apply() {
with_clean_overrides(|| {
RlxEnv::new()
.set("MPSGRAPH_MIN_FLOPS", "42")
.flag("USE_ICB", true)
.apply();
assert_eq!(parse_or("RLX_MPSGRAPH_MIN_FLOPS", 0u64), 42);
assert!(flag("RLX_USE_ICB"));
});
}
}