claude_agent/config/
env.rs1use super::provider::ConfigProvider;
7use super::{ConfigError, ConfigResult};
8
9#[derive(Debug, Clone)]
14pub struct EnvConfigProvider {
15 prefix: Option<String>,
16}
17
18impl EnvConfigProvider {
19 pub fn new() -> Self {
21 Self { prefix: None }
22 }
23
24 pub fn with_prefix(prefix: impl Into<String>) -> Self {
26 Self {
27 prefix: Some(prefix.into()),
28 }
29 }
30
31 fn env_key(&self, key: &str) -> String {
33 match &self.prefix {
34 Some(prefix) => format!("{}{}", prefix, key.to_uppercase().replace('.', "_")),
35 None => key.to_uppercase().replace('.', "_"),
36 }
37 }
38
39 fn key_from_env(&self, env_name: &str) -> Option<String> {
41 match &self.prefix {
42 Some(prefix) => {
43 if env_name.starts_with(prefix) {
44 Some(env_name[prefix.len()..].to_lowercase().replace('_', "."))
45 } else {
46 None
47 }
48 }
49 None => Some(env_name.to_lowercase().replace('_', ".")),
50 }
51 }
52}
53
54impl Default for EnvConfigProvider {
55 fn default() -> Self {
56 Self::new()
57 }
58}
59
60#[async_trait::async_trait]
61impl ConfigProvider for EnvConfigProvider {
62 fn name(&self) -> &str {
63 "env"
64 }
65
66 async fn get_raw(&self, key: &str) -> ConfigResult<Option<String>> {
67 let env_key = self.env_key(key);
68 match std::env::var(&env_key) {
69 Ok(value) => Ok(Some(value)),
70 Err(std::env::VarError::NotPresent) => Ok(None),
71 Err(e) => Err(ConfigError::Env(e)),
72 }
73 }
74
75 async fn set_raw(&self, _key: &str, _value: &str) -> ConfigResult<()> {
76 Err(ConfigError::Provider {
77 message: "Environment variables are read-only at runtime".into(),
78 })
79 }
80
81 async fn delete(&self, _key: &str) -> ConfigResult<bool> {
82 Err(ConfigError::Provider {
83 message: "Environment variables are read-only at runtime".into(),
84 })
85 }
86
87 async fn list_keys(&self, prefix: &str) -> ConfigResult<Vec<String>> {
88 let env_prefix = self.env_key(prefix);
89 let keys: Vec<String> = std::env::vars()
90 .filter_map(|(k, _)| {
91 if k.starts_with(&env_prefix) {
92 self.key_from_env(&k)
93 } else {
94 None
95 }
96 })
97 .collect();
98 Ok(keys)
99 }
100}
101
102#[cfg(test)]
103mod tests {
104 use super::*;
105
106 #[tokio::test]
107 async fn test_env_key_conversion() {
108 let provider = EnvConfigProvider::new();
109 assert_eq!(provider.env_key("api.key"), "API_KEY");
110 assert_eq!(provider.env_key("model.name"), "MODEL_NAME");
111
112 let provider = EnvConfigProvider::with_prefix("CLAUDE_");
113 assert_eq!(provider.env_key("api.key"), "CLAUDE_API_KEY");
114 }
115
116 #[tokio::test]
117 async fn test_env_provider_get() {
118 let provider = EnvConfigProvider::with_prefix("TEST_CONFIG_");
119
120 unsafe { std::env::set_var("TEST_CONFIG_MY_KEY", "my_value") };
122 let value = provider.get_raw("my.key").await.unwrap();
123 assert_eq!(value, Some("my_value".to_string()));
124 unsafe { std::env::remove_var("TEST_CONFIG_MY_KEY") };
125 }
126
127 #[tokio::test]
128 async fn test_env_provider_read_only() {
129 let provider = EnvConfigProvider::new();
130
131 assert!(provider.set_raw("key", "value").await.is_err());
133
134 assert!(provider.delete("key").await.is_err());
136 }
137
138 #[tokio::test]
139 async fn test_env_provider_not_found() {
140 let provider = EnvConfigProvider::with_prefix("NONEXISTENT_PREFIX_");
141 let value = provider.get_raw("some.key").await.unwrap();
142 assert_eq!(value, None);
143 }
144}