1use std::collections::HashMap;
4use std::env;
5use std::fs;
6use std::path::Path;
7
8#[derive(Debug, Clone, Default)]
10pub struct Config {
11 pub openai_api_key: Option<String>,
12 pub anthropic_api_key: Option<String>,
13 pub default_model: Option<String>,
14}
15
16impl Config {
17 pub fn load() -> Self {
19 Self::load_from_dir(Path::new("."))
20 }
21
22 pub fn load_from_dir(dir: &Path) -> Self {
24 let mut config = Self::default();
25
26 let env_file = dir.join(".gent.env");
28 if env_file.exists() {
29 if let Ok(contents) = fs::read_to_string(&env_file) {
30 let file_vars = Self::parse_env_file(&contents);
31 config.apply_vars(&file_vars);
32 }
33 }
34
35 config.apply_env_vars();
37
38 config
39 }
40
41 fn parse_env_file(contents: &str) -> HashMap<String, String> {
42 let mut vars = HashMap::new();
43 for line in contents.lines() {
44 let line = line.trim();
45 if line.is_empty() || line.starts_with('#') {
46 continue;
47 }
48 if let Some((key, value)) = line.split_once('=') {
49 vars.insert(key.trim().to_string(), value.trim().to_string());
50 }
51 }
52 vars
53 }
54
55 fn apply_vars(&mut self, vars: &HashMap<String, String>) {
56 if let Some(key) = vars.get("OPENAI_API_KEY") {
57 self.openai_api_key = Some(key.clone());
58 }
59 if let Some(key) = vars.get("ANTHROPIC_API_KEY") {
60 self.anthropic_api_key = Some(key.clone());
61 }
62 if let Some(model) = vars.get("GENT_DEFAULT_MODEL") {
63 self.default_model = Some(model.clone());
64 }
65 }
66
67 fn apply_env_vars(&mut self) {
68 if let Ok(key) = env::var("OPENAI_API_KEY") {
69 self.openai_api_key = Some(key);
70 }
71 if let Ok(key) = env::var("ANTHROPIC_API_KEY") {
72 self.anthropic_api_key = Some(key);
73 }
74 if let Ok(model) = env::var("GENT_DEFAULT_MODEL") {
75 self.default_model = Some(model);
76 }
77 }
78
79 pub fn require_openai_key(&self) -> Result<&str, crate::errors::GentError> {
81 self.openai_api_key
82 .as_deref()
83 .ok_or_else(|| crate::errors::GentError::MissingApiKey {
84 provider: "OPENAI".to_string(),
85 })
86 }
87}