1use std::cell::RefCell;
5use std::ffi::{OsStr, OsString};
6
7use winnow::stream::BStr;
8
9use crate::error::Error;
10use crate::value::FromArg;
11
12thread_local! {
13 static OVERRIDE: RefCell<Option<Vec<(OsString, OsString)>>> = const { RefCell::new(None) };
14}
15
16pub fn with_env<R>(vars: &[(&str, &str)], f: impl FnOnce() -> R) -> R {
20 let vars = vars
21 .iter()
22 .map(|(k, v)| (OsString::from(k), OsString::from(v)))
23 .collect();
24 struct Restore(Option<Vec<(OsString, OsString)>>);
26 impl Drop for Restore {
27 fn drop(&mut self) {
28 OVERRIDE.with(|o| *o.borrow_mut() = self.0.take());
29 }
30 }
31 let _restore = Restore(OVERRIDE.with(|o| o.replace(Some(vars))));
32 f()
33}
34
35pub(crate) fn overridden() -> bool {
38 OVERRIDE.with(|o| o.borrow().is_some())
39}
40
41pub fn var(name: &str) -> Option<OsString> {
43 OVERRIDE.with(|o| match &*o.borrow() {
44 Some(vars) => vars
45 .iter()
46 .find(|(k, _)| k == OsStr::new(name))
47 .map(|(_, v)| v.clone()),
48 None => std::env::var_os(name),
49 })
50}
51
52pub fn truthy(value: &OsStr) -> bool {
54 !matches!(
55 value.as_encoded_bytes(),
56 b"" | b"0" | b"false" | b"no" | b"off"
57 )
58}
59
60pub fn value<T: FromArg>(name: &str) -> Result<Option<T>, Error> {
62 let Some(raw) = var(name) else {
63 return Ok(None);
64 };
65 let bytes = BStr::new(raw.as_encoded_bytes());
66 T::from_arg(bytes)
67 .map(Some)
68 .map_err(|cause| Error::invalid_value(0, format!("${name}"), bytes, cause))
69}