gent/
config.rs

1//! Configuration loading for GENT
2
3use std::collections::HashMap;
4use std::env;
5use std::fs;
6use std::path::Path;
7
8/// GENT configuration
9#[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    /// Load configuration from environment and .gent.env file
18    pub fn load() -> Self {
19        Self::load_from_dir(Path::new("."))
20    }
21
22    /// Load configuration from a specific directory
23    pub fn load_from_dir(dir: &Path) -> Self {
24        let mut config = Self::default();
25
26        // First, load from .gent.env file (lower priority)
27        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        // Then, load from environment variables (higher priority)
36        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    /// Get OpenAI API key or return error
80    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}