use std::collections::HashMap;
use std::env;
use std::path::PathBuf;
#[derive(Debug, Clone)]
pub struct EnvironmentConfig {
pub environment: String,
pub data_dir: PathBuf,
pub config_dir: PathBuf,
pub cache_dir: PathBuf,
pub log_dir: PathBuf,
pub secrets: HashMap<String, String>,
pub features: HashMap<String, bool>,
pub performance: PerformanceConfig,
}
#[derive(Debug, Clone)]
pub struct PerformanceConfig {
pub max_concurrency: usize,
pub cache_size: usize,
pub timeouts: TimeoutConfig,
pub batching: BatchConfig,
}
#[derive(Debug, Clone)]
pub struct TimeoutConfig {
pub operation_ms: u64,
pub io_ms: u64,
pub network_ms: u64,
pub database_ms: u64,
}
#[derive(Debug, Clone)]
pub struct BatchConfig {
pub max_size: usize,
pub max_wait_ms: u64,
pub enabled: bool,
}
impl Default for EnvironmentConfig {
fn default() -> Self {
Self::new("development").unwrap_or_else(|_| Self::development())
}
}
impl EnvironmentConfig {
pub fn new(environment: &str) -> Result<Self, String> {
let home_dir = dirs::home_dir().ok_or("Could not determine home directory")?;
let data_dir = home_dir.join(".ggen").join("data");
let config_dir = home_dir.join(".ggen").join("config");
let cache_dir = home_dir.join(".ggen").join("cache");
let log_dir = home_dir.join(".ggen").join("logs");
for dir in [&data_dir, &config_dir, &cache_dir, &log_dir] {
std::fs::create_dir_all(dir)
.map_err(|e| format!("Failed to create directory {}: {}", dir.display(), e))?;
}
let mut secrets = HashMap::new();
let secret_names = [
"API_KEY",
"DATABASE_URL",
"SECRET_KEY",
"JWT_SECRET",
"OPENAI_API_KEY",
"ANTHROPIC_API_KEY",
"GITHUB_TOKEN",
];
for secret_name in &secret_names {
if let Ok(value) = env::var(secret_name) {
secrets.insert(secret_name.to_lowercase(), value);
}
}
let mut features = HashMap::new();
features.insert("experimental".to_string(), false);
features.insert("debug_logging".to_string(), environment == "development");
features.insert("cache_enabled".to_string(), true);
Ok(Self {
environment: environment.to_string(),
data_dir,
config_dir,
cache_dir,
log_dir,
secrets,
features,
performance: PerformanceConfig::default(),
})
}
pub fn development() -> Self {
Self {
environment: "development".to_string(),
data_dir: PathBuf::from("./dev-data"),
config_dir: PathBuf::from("./dev-config"),
cache_dir: PathBuf::from("./dev-cache"),
log_dir: PathBuf::from("./dev-logs"),
secrets: HashMap::new(),
features: {
let mut features = HashMap::new();
features.insert("experimental".to_string(), true);
features.insert("debug_logging".to_string(), true);
features.insert("cache_enabled".to_string(), true);
features
},
performance: PerformanceConfig {
max_concurrency: 4,
cache_size: 1000,
timeouts: TimeoutConfig {
operation_ms: 30000,
io_ms: 10000,
network_ms: 5000,
database_ms: 15000,
},
batching: BatchConfig {
max_size: 100,
max_wait_ms: 100,
enabled: true,
},
},
}
}
pub fn production() -> Self {
Self {
environment: "production".to_string(),
data_dir: PathBuf::from("/var/lib/ggen"),
config_dir: PathBuf::from("/etc/ggen"),
cache_dir: PathBuf::from("/var/cache/ggen"),
log_dir: PathBuf::from("/var/log/ggen"),
secrets: HashMap::new(),
features: {
let mut features = HashMap::new();
features.insert("experimental".to_string(), false);
features.insert("debug_logging".to_string(), false);
features.insert("cache_enabled".to_string(), true);
features.insert("telemetry_enabled".to_string(), true);
features
},
performance: PerformanceConfig {
max_concurrency: 16,
cache_size: 10000,
timeouts: TimeoutConfig {
operation_ms: 5000,
io_ms: 2000,
network_ms: 3000,
database_ms: 10000,
},
batching: BatchConfig {
max_size: 1000,
max_wait_ms: 50,
enabled: true,
},
},
}
}
pub fn get_secret(&self, key: &str) -> Option<&str> {
self.secrets.get(key).map(|s| s.as_str())
}
pub fn is_feature_enabled(&self, feature: &str) -> bool {
self.features.get(feature).copied().unwrap_or(false)
}
pub fn performance(&self) -> &PerformanceConfig {
&self.performance
}
pub fn module_data_dir(&self, module: &str) -> PathBuf {
self.data_dir.join(module)
}
}
impl Default for PerformanceConfig {
fn default() -> Self {
Self {
max_concurrency: 8,
cache_size: 5000,
timeouts: TimeoutConfig::default(),
batching: BatchConfig::default(),
}
}
}
impl Default for TimeoutConfig {
fn default() -> Self {
Self {
operation_ms: 30000,
io_ms: 10000,
network_ms: 5000,
database_ms: 15000,
}
}
}
impl Default for BatchConfig {
fn default() -> Self {
Self {
max_size: 100,
max_wait_ms: 100,
enabled: true,
}
}
}
#[derive(Debug)]
pub struct EnvironmentContext {
pub config: EnvironmentConfig,
}
impl EnvironmentContext {
pub fn new(config: EnvironmentConfig) -> Self {
Self { config }
}
pub fn clone_config(&self) -> EnvironmentConfig {
self.config.clone()
}
pub fn environment(&self) -> &str {
&self.config.environment
}
pub fn is_development(&self) -> bool {
self.config.environment == "development"
}
pub fn is_production(&self) -> bool {
self.config.environment == "production"
}
}
impl Clone for EnvironmentContext {
fn clone(&self) -> Self {
Self::new(self.config.clone())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_development_config() {
let config = EnvironmentConfig::development();
assert_eq!(config.environment, "development");
assert!(config.is_feature_enabled("debug_logging"));
}
#[test]
fn test_production_config() {
let config = EnvironmentConfig::production();
assert_eq!(config.environment, "production");
assert!(!config.is_feature_enabled("debug_logging"));
assert!(config.is_feature_enabled("telemetry_enabled"));
}
#[test]
fn test_secrets() {
env::set_var("API_KEY", "test_value");
let config = EnvironmentConfig::new("development").unwrap();
assert_eq!(config.get_secret("api_key"), Some("test_value"));
env::remove_var("API_KEY");
}
#[test]
fn test_context() {
let config = EnvironmentConfig::development();
let context = EnvironmentContext::new(config);
assert_eq!(context.environment(), "development");
assert!(context.is_development());
assert!(!context.is_production());
}
}