use crate::config::models::{AppConfig, ModelConfig};
use anyhow::Context;
use std::fs;
use std::path::{Path, PathBuf};
const CONFIG_DIR_NAME: &str = ".agentswitch";
const CONFIG_FILE_NAME: &str = "config.toml";
pub struct ConfigStore {
config_path: PathBuf,
config: AppConfig,
}
impl ConfigStore {
pub fn new() -> anyhow::Result<Self> {
let config_dir = ConfigStore::get_config_dir()?;
let config_path = config_dir.join(CONFIG_FILE_NAME);
Self::ensure_initialized(&config_dir)?;
let config = Self::load_config(&config_path)?;
Ok(Self {
config_path,
config,
})
}
fn get_config_dir() -> anyhow::Result<PathBuf> {
let mut config_dir =
dirs::home_dir().ok_or_else(|| anyhow::anyhow!("无法获取用户主目录"))?;
config_dir.push(CONFIG_DIR_NAME);
Ok(config_dir)
}
fn ensure_initialized(config_dir: &Path) -> anyhow::Result<()> {
if !config_dir.exists() {
println!("⚠️ 首次使用,正在自动创建配置...");
fs::create_dir_all(config_dir).context("无法创建配置目录")?;
println!("✓ 配置目录已创建: {}", config_dir.display());
let config_path = config_dir.join(CONFIG_FILE_NAME);
let default_config = AppConfig::new();
let toml_str = toml::to_string_pretty(&default_config).context("无法序列化默认配置")?;
fs::write(&config_path, toml_str).context("无法写入配置文件")?;
println!("✓ 配置文件已创建: {}", config_path.display());
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&config_path)?.permissions();
perms.set_mode(0o600);
fs::set_permissions(&config_path, perms)?;
println!("✓ 文件权限已设置: 0600(仅所有者可读写)");
}
}
Ok(())
}
fn load_config(config_path: &Path) -> anyhow::Result<AppConfig> {
if !config_path.exists() {
return Ok(AppConfig::new());
}
let content = fs::read_to_string(config_path).context("无法读取配置文件")?;
let config: AppConfig =
toml::from_str(&content).context("配置文件格式错误,请检查 TOML 格式")?;
Ok(config)
}
pub fn save(&self) -> anyhow::Result<()> {
if let Some(config_dir) = self.config_path.parent() {
fs::create_dir_all(config_dir).context("无法创建配置目录")?;
}
let toml_str = toml::to_string_pretty(&self.config).context("无法序列化配置")?;
fs::write(&self.config_path, toml_str).context("无法写入配置文件")?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
if let Ok(mut perms) = fs::metadata(&self.config_path).map(|m| m.permissions()) {
perms.set_mode(0o600);
if let Err(e) = fs::set_permissions(&self.config_path, perms) {
eprintln!("⚠️ 警告: 无法设置文件权限: {}", e);
}
}
}
Ok(())
}
pub fn add_model(&mut self, model: ModelConfig) -> anyhow::Result<()> {
self.config.add_model(model)?;
self.save()
}
pub fn list_models(&self) -> &[ModelConfig] {
&self.config.models
}
pub fn remove_model(&mut self, name: &str) -> anyhow::Result<()> {
self.config.remove_model(name)?;
self.save()
}
pub fn edit_model<F>(&mut self, name: &str, updater: F) -> anyhow::Result<()>
where
F: FnOnce(&mut ModelConfig) -> anyhow::Result<()>,
{
self.config.edit_model(name, updater)?;
self.save()
}
pub fn get_all_active_models(&self) -> &std::collections::HashMap<String, String> {
&self.config.active_models
}
pub fn update_active_model(
&mut self,
agent_name: &str,
model_name: &str,
) -> anyhow::Result<()> {
self.config
.active_models
.insert(agent_name.to_string(), model_name.to_string());
self.save()
}
#[allow(dead_code)]
pub fn get_active_model(&self, agent_name: &str) -> Option<&String> {
self.config.active_models.get(agent_name)
}
pub fn has_model(&self, name: &str) -> bool {
self.config.models.iter().any(|m| m.name == name)
}
pub fn get_model(&self, name: &str) -> Option<&ModelConfig> {
self.config.models.iter().find(|m| m.name == name)
}
pub fn load_all(&self) -> std::collections::HashMap<String, ModelConfig> {
self.config
.models
.iter()
.map(|m| (m.name.clone(), m.clone()))
.collect()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_config_dir_path() {
let config_dir = ConfigStore::get_config_dir().unwrap();
assert!(config_dir.ends_with(".agentswitch"));
}
}