use std::collections::HashMap;
use std::fmt;
pub trait EnvSource: Send + Sync {
fn var(&self, name: &str) -> Option<String>;
}
#[derive(Clone, Copy, Debug, Default)]
pub struct ProcessEnv;
impl EnvSource for ProcessEnv {
fn var(&self, name: &str) -> Option<String> {
std::env::var(name).ok()
}
}
#[derive(Clone, Default, PartialEq, Eq)]
pub struct MapEnv(HashMap<String, String>);
impl MapEnv {
#[must_use]
pub fn new() -> Self {
Self::default()
}
#[must_use]
pub fn with(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
self.0.insert(name.into(), value.into());
self
}
}
impl fmt::Debug for MapEnv {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut names: Vec<&String> = self.0.keys().collect();
names.sort();
f.debug_tuple("MapEnv").field(&names).finish()
}
}
impl<K: Into<String>, V: Into<String>> FromIterator<(K, V)> for MapEnv {
fn from_iter<I: IntoIterator<Item = (K, V)>>(iter: I) -> Self {
Self(
iter.into_iter()
.map(|(k, v)| (k.into(), v.into()))
.collect(),
)
}
}
impl EnvSource for MapEnv {
fn var(&self, name: &str) -> Option<String> {
self.0.get(name).cloned()
}
}
pub(crate) fn lookup(
name: &str,
explicit: Option<&indexmap::IndexMap<String, String>>,
env: &dyn EnvSource,
) -> Option<String> {
explicit
.and_then(|map| map.get(name))
.filter(|value| !value.is_empty())
.cloned()
.or_else(|| env.var(name).filter(|value| !value.is_empty()))
}