use serde::{Deserialize, Serialize};
use std::env;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AppConfig {
pub db_path: String,
pub web_bind: String,
pub output_mode: String,
pub message_channel_capacity: usize,
pub default_api_base_url: String,
pub default_model: String,
pub log_level: String,
pub run_agent_loops: bool,
}
impl Default for AppConfig {
fn default() -> Self {
Self {
db_path: get_env_or_default("DB_PATH", "imitatort.db".to_string()),
web_bind: get_env_or_default("WEB_BIND", "0.0.0.0:8080".to_string()),
output_mode: get_env_or_default("OUTPUT_MODE", "cli".to_string()),
message_channel_capacity: get_env_or_default("MESSAGE_CHANNEL_CAPACITY", 1000usize),
default_api_base_url: get_env_or_default(
"DEFAULT_API_BASE_URL",
"https://api.openai.com/v1".to_string(),
),
default_model: get_env_or_default("DEFAULT_MODEL", "gpt-4o-mini".to_string()),
log_level: get_env_or_default("LOG_LEVEL", "info".to_string()),
run_agent_loops: get_env_or_default("RUN_AGENT_LOOPS", true), }
}
}
impl AppConfig {
pub fn from_env() -> Self {
Self::default()
}
pub fn from_file(path: &str) -> Result<Self, Box<dyn std::error::Error>> {
let content = std::fs::read_to_string(path)?;
let config: AppConfig = serde_json::from_str(&content)?;
Ok(config)
}
pub fn load(config_path: Option<&str>) -> Self {
match config_path {
Some(path) => Self::from_file(path).unwrap_or_else(|_| Self::from_env()),
None => Self::from_env(),
}
}
}
fn get_env_or_default<T: std::str::FromStr + Default>(key: &str, default: T) -> T {
env::var(key)
.ok()
.and_then(|val| val.parse().ok())
.unwrap_or(default)
}