use std::collections::HashMap;
use std::env;
use serde_json::{Value, json};
use super::ConfigProvider;
use super::ConfigError;
#[derive(Debug)]
pub struct EnvConfigProvider {
prefix: String,
cache: HashMap<String, String>,
}
impl EnvConfigProvider {
pub fn new(prefix: &str) -> Self {
let mut provider = Self {
prefix: prefix.to_string(),
cache: HashMap::new(),
};
provider.refresh_cache();
provider
}
pub fn default() -> Self {
Self::new("FOXY_")
}
pub fn refresh_cache(&mut self) {
self.cache.clear();
for (key, value) in env::vars() {
if key.starts_with(&self.prefix) {
let config_key = key[self.prefix.len()..].to_lowercase();
let config_key = config_key.replace('_', ".");
self.cache.insert(config_key, value);
}
}
}
fn parse_value_to_json(&self, value: &str) -> Result<Value, ConfigError> {
if let Ok(json_value) = serde_json::from_str(value) {
return Ok(json_value);
}
if value.eq_ignore_ascii_case("true") {
return Ok(json!(true));
} else if value.eq_ignore_ascii_case("false") {
return Ok(json!(false));
}
if let Ok(int_val) = value.parse::<i64>() {
return Ok(json!(int_val));
}
if let Ok(float_val) = value.parse::<f64>() {
return Ok(json!(float_val));
}
Ok(json!(value))
}
}
impl ConfigProvider for EnvConfigProvider {
fn get_raw(&self, key: &str) -> Result<Option<Value>, ConfigError> {
match self.cache.get(key) {
Some(value) => self.parse_value_to_json(value).map(Some),
None => Ok(None),
}
}
fn has(&self, key: &str) -> bool {
self.cache.contains_key(key)
}
fn provider_name(&self) -> &str {
"env"
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use crate::config::ConfigProviderExt;
#[test]
fn test_env_provider() {
unsafe {
env::set_var("FOXY_SERVER_HOST", "localhost");
env::set_var("FOXY_SERVER_PORT", "9090");
env::set_var("FOXY_DEBUG", "true");
}
let provider = EnvConfigProvider::default();
assert_eq!(provider.has("server.host"), true);
assert_eq!(provider.has("nonexistent"), false);
let host: String = provider.get("server.host").unwrap().unwrap();
assert_eq!(host, "localhost");
let port: u16 = provider.get("server.port").unwrap().unwrap();
assert_eq!(port, 9090);
let debug: bool = provider.get("debug").unwrap().unwrap();
assert_eq!(debug, true);
unsafe {
env::remove_var("FOXY_SERVER_HOST");
env::remove_var("FOXY_SERVER_PORT");
env::remove_var("FOXY_DEBUG");
}
}
#[test]
fn test_custom_prefix() {
unsafe {
env::set_var("CUSTOM_HOST", "customhost");
}
let provider = EnvConfigProvider::new("CUSTOM_");
assert_eq!(provider.has("host"), true);
let host: String = provider.get("host").unwrap().unwrap();
assert_eq!(host, "customhost");
unsafe {
env::remove_var("CUSTOM_HOST");
}
}
#[test]
fn test_cache_refresh() {
let mut provider = EnvConfigProvider::new("TEST_");
assert_eq!(provider.has("value"), false);
unsafe {
env::set_var("TEST_VALUE", "42");
}
assert_eq!(provider.has("value"), false);
provider.refresh_cache();
assert_eq!(provider.has("value"), true);
let value: i32 = provider.get("value").unwrap().unwrap();
assert_eq!(value, 42);
unsafe {
env::remove_var("TEST_VALUE");
}
}
}