use std::cell::RefCell;
use std::ffi::{OsStr, OsString};
use winnow::stream::BStr;
use crate::error::Error;
use crate::value::FromArg;
thread_local! {
static OVERRIDE: RefCell<Option<Vec<(OsString, OsString)>>> = const { RefCell::new(None) };
}
pub fn with_env<R>(vars: &[(&str, &str)], f: impl FnOnce() -> R) -> R {
let vars = vars
.iter()
.map(|(k, v)| (OsString::from(k), OsString::from(v)))
.collect();
struct Restore(Option<Vec<(OsString, OsString)>>);
impl Drop for Restore {
fn drop(&mut self) {
OVERRIDE.with(|o| *o.borrow_mut() = self.0.take());
}
}
let _restore = Restore(OVERRIDE.with(|o| o.replace(Some(vars))));
f()
}
pub(crate) fn overridden() -> bool {
OVERRIDE.with(|o| o.borrow().is_some())
}
pub fn var(name: &str) -> Option<OsString> {
OVERRIDE.with(|o| match &*o.borrow() {
Some(vars) => vars
.iter()
.find(|(k, _)| k == OsStr::new(name))
.map(|(_, v)| v.clone()),
None => std::env::var_os(name),
})
}
pub fn truthy(value: &OsStr) -> bool {
!matches!(
value.as_encoded_bytes(),
b"" | b"0" | b"false" | b"no" | b"off"
)
}
pub fn value<T: FromArg>(name: &str) -> Result<Option<T>, Error> {
let Some(raw) = var(name) else {
return Ok(None);
};
let bytes = BStr::new(raw.as_encoded_bytes());
T::from_arg(bytes)
.map(Some)
.map_err(|cause| Error::invalid_value(0, format!("${name}"), bytes, cause))
}