use crate::agents::{AgentAdapter, Backup};
use crate::backup::BackupManager;
use crate::config::ModelConfig;
use anyhow::{Context, Result};
use std::fs;
use std::path::PathBuf;
pub struct MyToolAdapter;
impl MyToolAdapter {
pub fn new() -> Self {
Self
}
fn config_dir(&self) -> Result<PathBuf> {
let home = dirs::home_dir()
.context("无法获取用户主目录")?;
Ok(home.join(".mytool"))
}
}
impl AgentAdapter for MyToolAdapter {
fn name(&self) -> &str {
"mytool"
}
fn detect(&self) -> Result<bool> {
if let Ok(Some(_)) = which::which("mytool") {
return Ok(true);
}
if let Ok(home) = dirs::home_dir() {
let npm_global = home.join(".npm-global")
.join("bin")
.join("mytool");
if npm_global.exists() {
return Ok(true);
}
}
Ok(self.config_dir()?.exists())
}
fn config_path(&self) -> Result<PathBuf> {
Ok(self.config_dir()?.join("config.json"))
}
fn current_model(&self) -> Result<Option<String>> {
let config_path = self.config_path()?;
if !config_path.exists() {
return Ok(None);
}
let content = fs::read_to_string(&config_path)
.context("读取配置文件失败")?;
let json: serde_json::Value = serde_json::from_str(&content)
.context("解析配置文件失败")?;
if let Some(model) = json.get("model").and_then(|v| v.as_str()) {
return Ok(Some(model.to_string()));
}
Ok(None)
}
fn backup(&self) -> Result<Backup> {
let config_path = self.config_path()?;
let backup_manager = BackupManager::new()?;
backup_manager.create_backup(
self.name(),
&config_path,
"json" )
}
fn apply(&self, model_config: &ModelConfig) -> Result<()> {
let config_path = self.config_path()?;
let mut config = if config_path.exists() {
let content = fs::read_to_string(&config_path)
.context("读取配置文件失败")?;
serde_json::from_str::<serde_json::Value>(&content)
.unwrap_or_else(|_| serde_json::json!({}))
} else {
serde_json::json!({})
};
if !config.is_object() {
*config = serde_json::json!({});
}
let config_obj = config.as_object_mut()
.context("配置根节点不是对象")?;
config_obj.insert("api_key".to_string(),
serde_json::json!(model_config.api_key.clone()));
config_obj.insert("base_url".to_string(),
serde_json::json!(model_config.base_url.clone()));
config_obj.insert("model".to_string(),
serde_json::json!(model_config.model_id.clone()));
if let Some(config_dir) = config_path.parent() {
fs::create_dir_all(config_dir)
.context("创建配置目录失败")?;
}
let content = serde_json::to_string_pretty(&config)
.context("序列化配置失败")?;
fs::write(&config_path, content)
.context("写入配置文件失败")?;
#[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)?;
}
Ok(())
}
fn restore(&self, backup: &Backup) -> Result<()> {
let backup_manager = BackupManager::new()?;
backup_manager.restore_backup(backup)
}
}
impl Default for MyToolAdapter {
fn default() -> Self {
Self::new()
}
}
#[allow(dead_code)]
pub struct MyToolYamlAdapter;
#[allow(dead_code)]
impl MyToolYamlAdapter {
fn apply_yaml(&self, model_config: &ModelConfig) -> Result<()> {
use std::io::Write;
let config_path = dirs::home_dir()
.context("无法获取用户主目录")?
.join(".mytool")
.join("config.yaml");
let yaml_content = format!(
r#"# MyTool Configuration
api_key: {}
base_url: {}
model: {}
"#,
model_config.api_key,
model_config.base_url,
model_config.model_id
);
fs::write(&config_path, yaml_content)
.context("写入 YAML 配置失败")?;
Ok(())
}
}
#[allow(dead_code)]
pub struct MyToolMultiFileAdapter;
#[allow(dead_code)]
impl MyToolMultiFileAdapter {
fn apply_multi_file(&self, model_config: &ModelConfig) -> Result<()> {
let config_dir = dirs::home_dir()
.context("无法获取用户主目录")?
.join(".mytool");
fs::create_dir_all(&config_dir)
.context("创建配置目录失败")?;
let main_config = config_dir.join("config.json");
let main_config_content = serde_json::json!({
"model": model_config.model_id,
});
fs::write(&main_config, serde_json::to_string_pretty(&main_config_content)?)
.context("写入主配置失败")?;
let auth_config = config_dir.join("auth.json");
let auth_config_content = serde_json::json!({
"api_key": model_config.api_key,
});
fs::write(&auth_config, serde_json::to_string_pretty(&auth_config_content)?)
.context("写入认证配置失败")?;
let env_config = config_dir.join(".env");
let env_content = format!(
"MYTOOL_BASE_URL={}\nMYTOOL_MODEL={}\n",
model_config.base_url,
model_config.model_id
);
fs::write(&env_config, env_content)
.context("写入环境配置失败")?;
Ok(())
}
}
#[allow(dead_code)]
pub struct MyToolEnvAdapter;
#[allow(dead_code)]
impl MyToolEnvAdapter {
fn apply_env(&self, model_config: &ModelConfig) -> Result<()> {
let config_dir = dirs::home_dir()
.context("无法获取用户主目录")?
.join(".mytool");
fs::create_dir_all(&config_dir)
.context("创建配置目录失败")?;
let env_path = config_dir.join(".env");
let env_content = format!(
r#"# MyTool Environment Variables
MYTOOL_API_KEY={}
MYTOOL_BASE_URL={}
MYTOOL_MODEL={}
"#,
model_config.api_key,
model_config.base_url,
model_config.model_id
);
fs::write(&env_path, env_content)
.context("写入 .env 文件失败")?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&env_path)?.permissions();
perms.set_mode(0o600);
fs::set_permissions(&env_path, perms)?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_adapter_name() {
let adapter = MyToolAdapter::new();
assert_eq!(adapter.name(), "mytool");
}
#[test]
fn test_config_path() {
let adapter = MyToolAdapter::new();
let path = adapter.config_path().unwrap();
assert!(path.ends_with(".mytool/config.json"));
}
#[test]
fn test_current_model_no_config() {
let adapter = MyToolAdapter::new();
let model = adapter.current_model().unwrap();
assert!(model.is_none());
}
}