use std::collections::HashMap;
use std::env;
use anyhow::Result;
use config::{ConfigError, Source, Value};
const PREFIX_PATTERN: &str = "CF_";
pub trait QueryEnvironment {
fn get_var(&self, var: &'static str) -> Result<String, std::env::VarError>;
fn empty(&self) -> Result<bool>;
}
#[derive(Clone, Debug)]
pub struct Environment {
whitelist: Vec<&'static str>,
}
impl Environment {
pub fn with_whitelist(whitelist: Vec<&'static str>) -> Self {
Environment { whitelist }
}
}
impl QueryEnvironment for Environment {
fn get_var(&self, var: &'static str) -> Result<String, std::env::VarError> {
env::var(var)
}
fn empty(&self) -> Result<bool> {
let env = self.collect()?;
Ok(env.is_empty())
}
}
impl Source for Environment {
fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
Box::new((*self).clone())
}
fn collect(&self) -> Result<HashMap<String, Value>, ConfigError> {
let mut m = HashMap::new();
let uri: String = "env".into();
for key in &self.whitelist {
if let Ok(value) = env::var(key) {
let key = key.strip_prefix(PREFIX_PATTERN).unwrap_or(key);
m.insert(key.to_lowercase(), Value::new(Some(&uri), value));
}
}
Ok(m)
}
}
#[derive(Clone, Debug, Default)]
#[cfg(test)]
pub struct MockEnvironment {
vars: Vec<(&'static str, &'static str)>,
}
#[cfg(test)]
impl MockEnvironment {
pub fn set(&mut self, key: &'static str, value: &'static str) -> &Self {
self.vars.push((key, value));
self
}
}
#[cfg(test)]
impl QueryEnvironment for MockEnvironment {
#[allow(unused_variables)]
fn get_var(&self, var: &'static str) -> Result<String, std::env::VarError> {
Ok("Some Mocked Result".to_string()) }
fn empty(&self) -> Result<bool> {
Ok(self.vars.is_empty())
}
}
#[cfg(test)]
impl Source for MockEnvironment {
fn clone_into_box(&self) -> Box<dyn Source + Send + Sync> {
Box::new((*self).clone())
}
fn collect(&self) -> Result<HashMap<String, Value>, ConfigError> {
let mut m = HashMap::new();
let uri: String = "env".into();
for (key, value) in &self.vars {
let prefix_pattern = "CF_";
let key = &key[prefix_pattern.len()..];
m.insert(key.to_lowercase(), Value::new(Some(&uri), *value));
}
Ok(m)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_gets_from_the_environment() {
env::set_var("CF_API_KEY", "waylongapikey");
env::set_var("CF_EMAIL", "user@example.com");
env::set_var("CF_IRRELEVANT", "irrelevant");
let environment = Environment::with_whitelist(vec!["CF_API_KEY", "CF_EMAIL"]);
let mut expected_env_vars: HashMap<String, Value> = HashMap::new();
expected_env_vars.insert(
"api_key".to_string(),
Value::new(Some(&"env".to_string()), "waylongapikey"),
);
expected_env_vars.insert(
"email".to_string(),
Value::new(Some(&"env".to_string()), "user@example.com"),
);
assert_eq!(environment.collect().unwrap(), expected_env_vars);
}
}