1use std::collections::HashMap;
8use std::env;
9use serde_json::{Value, json};
10
11use super::ConfigProvider;
12use super::ConfigError;
13
14#[derive(Debug)]
16pub struct EnvConfigProvider {
17 prefix: String,
19 cache: HashMap<String, String>,
21}
22
23impl EnvConfigProvider {
24 pub fn new(prefix: &str) -> Self {
26 let mut provider = Self {
27 prefix: prefix.to_string(),
28 cache: HashMap::new(),
29 };
30
31 provider.refresh_cache();
33
34 provider
35 }
36
37 pub fn default() -> Self {
39 Self::new("FOXY_")
40 }
41
42 pub fn refresh_cache(&mut self) {
44 self.cache.clear();
45
46 for (key, value) in env::vars() {
47 if key.starts_with(&self.prefix) {
48 let config_key = key[self.prefix.len()..].to_lowercase();
50 let config_key = config_key.replace('_', ".");
52
53 self.cache.insert(config_key, value);
54 }
55 }
56 }
57
58 fn parse_value_to_json(&self, value: &str) -> Result<Value, ConfigError> {
60 if let Ok(json_value) = serde_json::from_str(value) {
62 return Ok(json_value);
63 }
64
65 if value.eq_ignore_ascii_case("true") {
69 return Ok(json!(true));
70 } else if value.eq_ignore_ascii_case("false") {
71 return Ok(json!(false));
72 }
73
74 if let Ok(int_val) = value.parse::<i64>() {
76 return Ok(json!(int_val));
77 }
78
79 if let Ok(float_val) = value.parse::<f64>() {
80 return Ok(json!(float_val));
81 }
82
83 Ok(json!(value))
85 }
86}
87
88impl ConfigProvider for EnvConfigProvider {
89 fn get_raw(&self, key: &str) -> Result<Option<Value>, ConfigError> {
90 match self.cache.get(key) {
91 Some(value) => self.parse_value_to_json(value).map(Some),
92 None => Ok(None),
93 }
94 }
95
96 fn has(&self, key: &str) -> bool {
97 self.cache.contains_key(key)
98 }
99
100 fn provider_name(&self) -> &str {
101 "env"
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108 use std::env;
109 use crate::config::ConfigProviderExt;
110
111 #[test]
112 fn test_env_provider() {
113 unsafe {
115 env::set_var("FOXY_SERVER_HOST", "localhost");
116 env::set_var("FOXY_SERVER_PORT", "9090");
117 env::set_var("FOXY_DEBUG", "true");
118 }
119
120 let provider = EnvConfigProvider::default();
121
122 assert_eq!(provider.has("server.host"), true);
123 assert_eq!(provider.has("nonexistent"), false);
124
125 let host: String = provider.get("server.host").unwrap().unwrap();
126 assert_eq!(host, "localhost");
127
128 let port: u16 = provider.get("server.port").unwrap().unwrap();
129 assert_eq!(port, 9090);
130
131 let debug: bool = provider.get("debug").unwrap().unwrap();
132 assert_eq!(debug, true);
133
134 unsafe {
136 env::remove_var("FOXY_SERVER_HOST");
137 env::remove_var("FOXY_SERVER_PORT");
138 env::remove_var("FOXY_DEBUG");
139 }
140 }
141
142 #[test]
143 fn test_custom_prefix() {
144 unsafe {
145 env::set_var("CUSTOM_HOST", "customhost");
146 }
147
148 let provider = EnvConfigProvider::new("CUSTOM_");
149
150 assert_eq!(provider.has("host"), true);
151 let host: String = provider.get("host").unwrap().unwrap();
152 assert_eq!(host, "customhost");
153
154 unsafe {
156 env::remove_var("CUSTOM_HOST");
157 }
158 }
159
160 #[test]
161 fn test_cache_refresh() {
162 let mut provider = EnvConfigProvider::new("TEST_");
163
164 assert_eq!(provider.has("value"), false);
166
167 unsafe {
169 env::set_var("TEST_VALUE", "42");
170 }
171
172 assert_eq!(provider.has("value"), false);
174
175 provider.refresh_cache();
177
178 assert_eq!(provider.has("value"), true);
180 let value: i32 = provider.get("value").unwrap().unwrap();
181 assert_eq!(value, 42);
182
183 unsafe {
185 env::remove_var("TEST_VALUE");
186 }
187 }
188}