ricecoder_storage/config/
env.rs1use super::Config;
7use std::collections::HashMap;
8
9pub struct EnvOverrides;
11
12impl EnvOverrides {
13 pub fn parse() -> HashMap<String, String> {
19 let mut overrides = HashMap::new();
20
21 for (key, value) in std::env::vars() {
22 if let Some(config_key) = key.strip_prefix("RICECODER_") {
23 let config_path = Self::env_key_to_config_path(config_key);
24 overrides.insert(config_path, value);
25 }
26 }
27
28 overrides
29 }
30
31 pub fn apply(config: &mut Config) {
36 let overrides = Self::parse();
37 Self::apply_overrides(config, &overrides);
38 }
39
40 pub fn apply_overrides(config: &mut Config, overrides: &HashMap<String, String>) {
42 for (path, value) in overrides {
43 Self::set_config_value(config, path, value);
44 }
45 }
46
47 fn env_key_to_config_path(key: &str) -> String {
54 key.to_lowercase().replace('_', ".")
55 }
56
57 fn set_config_value(config: &mut Config, path: &str, value: &str) {
61 let parts: Vec<&str> = path.split('.').collect();
62
63 match parts.as_slice() {
64 ["providers", "default_provider"] => {
65 config.providers.default_provider = Some(value.to_string());
66 }
67 ["providers", "api_keys", key] => {
68 config
69 .providers
70 .api_keys
71 .insert(key.to_string(), value.to_string());
72 }
73 ["providers", "endpoints", key] => {
74 config
75 .providers
76 .endpoints
77 .insert(key.to_string(), value.to_string());
78 }
79 ["defaults", "model"] => {
80 config.defaults.model = Some(value.to_string());
81 }
82 ["defaults", "temperature"] => {
83 if let Ok(temp) = value.parse::<f32>() {
84 config.defaults.temperature = Some(temp);
85 }
86 }
87 ["defaults", "max_tokens"] => {
88 if let Ok(tokens) = value.parse::<u32>() {
89 config.defaults.max_tokens = Some(tokens);
90 }
91 }
92 _ => {
93 if let Ok(json_value) = serde_json::from_str(value) {
95 config.custom.insert(path.to_string(), json_value);
96 } else {
97 config.custom.insert(
98 path.to_string(),
99 serde_json::Value::String(value.to_string()),
100 );
101 }
102 }
103 }
104 }
105}
106
107#[cfg(test)]
108mod tests {
109 use super::*;
110
111 #[test]
112 fn test_env_key_to_config_path() {
113 assert_eq!(
114 EnvOverrides::env_key_to_config_path("PROVIDERS_DEFAULT"),
115 "providers.default"
116 );
117 assert_eq!(
118 EnvOverrides::env_key_to_config_path("DEFAULTS_MODEL"),
119 "defaults.model"
120 );
121 }
122
123 #[test]
124 fn test_apply_provider_default_override() {
125 let mut config = Config::default();
126 let mut overrides = HashMap::new();
127 overrides.insert(
128 "providers.default_provider".to_string(),
129 "openai".to_string(),
130 );
131
132 EnvOverrides::apply_overrides(&mut config, &overrides);
133
134 assert_eq!(
135 config.providers.default_provider,
136 Some("openai".to_string())
137 );
138 }
139
140 #[test]
141 fn test_apply_api_key_override() {
142 let mut config = Config::default();
143 let mut overrides = HashMap::new();
144 overrides.insert(
145 "providers.api_keys.openai".to_string(),
146 "test-key".to_string(),
147 );
148
149 EnvOverrides::apply_overrides(&mut config, &overrides);
150
151 assert_eq!(
152 config.providers.api_keys.get("openai"),
153 Some(&"test-key".to_string())
154 );
155 }
156
157 #[test]
158 fn test_apply_defaults_override() {
159 let mut config = Config::default();
160 let mut overrides = HashMap::new();
161 overrides.insert("defaults.model".to_string(), "gpt-4".to_string());
162 overrides.insert("defaults.temperature".to_string(), "0.7".to_string());
163 overrides.insert("defaults.max_tokens".to_string(), "2000".to_string());
164
165 EnvOverrides::apply_overrides(&mut config, &overrides);
166
167 assert_eq!(config.defaults.model, Some("gpt-4".to_string()));
168 assert_eq!(config.defaults.temperature, Some(0.7));
169 assert_eq!(config.defaults.max_tokens, Some(2000));
170 }
171
172 #[test]
173 fn test_apply_multiple_overrides() {
174 let mut config = Config::default();
175 let mut overrides = HashMap::new();
176 overrides.insert(
177 "providers.default_provider".to_string(),
178 "openai".to_string(),
179 );
180 overrides.insert("defaults.model".to_string(), "gpt-4".to_string());
181 overrides.insert(
182 "providers.api_keys.openai".to_string(),
183 "test-key".to_string(),
184 );
185
186 EnvOverrides::apply_overrides(&mut config, &overrides);
187
188 assert_eq!(
189 config.providers.default_provider,
190 Some("openai".to_string())
191 );
192 assert_eq!(config.defaults.model, Some("gpt-4".to_string()));
193 assert_eq!(
194 config.providers.api_keys.get("openai"),
195 Some(&"test-key".to_string())
196 );
197 }
198
199 #[test]
200 fn test_apply_custom_override() {
201 let mut config = Config::default();
202 let mut overrides = HashMap::new();
203 overrides.insert("custom.setting".to_string(), "value".to_string());
204
205 EnvOverrides::apply_overrides(&mut config, &overrides);
206
207 assert_eq!(
208 config.custom.get("custom.setting"),
209 Some(&serde_json::Value::String("value".to_string()))
210 );
211 }
212}