use std::cell::RefCell;
use std::collections::HashSet;
use std::ffi::OsString;
use std::{collections, env};
use subst::VariableMap;
use tracing::warn;
pub trait Env<'a>: VariableMap<'a> {
fn var_os(&self, key: &str) -> Option<OsString>;
#[must_use]
fn get_env_str(&self, key: &str) -> Option<String> {
match self.var_os(key) {
Some(s) => match s.into_string() {
Ok(v) => Some(v),
Err(v) => {
let v = v.to_string_lossy();
warn!(
"Environment variable {key} has invalid unicode. Lossy representation: {v}"
);
None
}
},
None => None,
}
}
#[must_use]
fn has_unused_var(&self, key: &str) -> bool;
}
#[derive(Debug, Default)]
pub struct OsEnv(RefCell<HashSet<String>>);
impl Env<'_> for OsEnv {
fn var_os(&self, key: &str) -> Option<OsString> {
env::var_os(key)
}
fn has_unused_var(&self, key: &str) -> bool {
!self.0.borrow().contains(key) && env::var_os(key).is_some()
}
}
impl<'a> VariableMap<'a> for OsEnv {
type Value = String;
fn get(&'a self, key: &str) -> Option<Self::Value> {
self.0.borrow_mut().insert(key.to_string());
env::var(key).ok()
}
}
#[derive(Debug, Default)]
pub struct FauxEnv(pub collections::HashMap<&'static str, OsString>);
impl<'a> VariableMap<'a> for FauxEnv {
type Value = String;
fn get(&'a self, key: &str) -> Option<Self::Value> {
self.0.get(key).map(|s| s.to_string_lossy().to_string())
}
}
impl Env<'_> for FauxEnv {
fn var_os(&self, key: &str) -> Option<OsString> {
self.0.get(key).map(Into::into)
}
fn has_unused_var(&self, key: &str) -> bool {
self.var_os(key).is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_env_str() {
let env = FauxEnv::default();
assert_eq!(env.get_env_str("FOO"), None);
let env = FauxEnv(vec![("FOO", OsString::from("bar"))].into_iter().collect());
assert_eq!(env.get_env_str("FOO"), Some("bar".to_string()));
}
#[test]
#[cfg(unix)]
fn test_bad_os_str() {
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt as _;
let bad_utf8 = [0x66, 0x6f, 0x80, 0x6f];
let os_str = OsStr::from_bytes(&bad_utf8[..]);
let env = FauxEnv(vec![("BAD", os_str.to_owned())].into_iter().collect());
assert!(env.0.contains_key("BAD"));
assert_eq!(env.get_env_str("BAD"), None);
}
}