feat: 实现 Rust 后端核心模块 - 配置管理、供应商管理和数据存储
This commit is contained in:
@@ -23,3 +23,6 @@ serde = { version = "1.0", features = ["derive"] }
|
|||||||
log = "0.4"
|
log = "0.4"
|
||||||
tauri = { version = "2.8.2" }
|
tauri = { version = "2.8.2" }
|
||||||
tauri-plugin-log = "2"
|
tauri-plugin-log = "2"
|
||||||
|
tauri-plugin-store = "2"
|
||||||
|
dirs = "5.0"
|
||||||
|
uuid = { version = "1.11", features = ["v4", "serde"] }
|
||||||
|
|||||||
137
src-tauri/src/config.rs
Normal file
137
src-tauri/src/config.rs
Normal file
@@ -0,0 +1,137 @@
|
|||||||
|
use std::fs;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::io::{self, Write};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
|
||||||
|
/// 获取 Claude Code 配置目录路径
|
||||||
|
pub fn get_claude_config_dir() -> PathBuf {
|
||||||
|
dirs::home_dir()
|
||||||
|
.expect("无法获取用户主目录")
|
||||||
|
.join(".claude")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 Claude Code 主配置文件路径
|
||||||
|
pub fn get_claude_settings_path() -> PathBuf {
|
||||||
|
get_claude_config_dir().join("settings.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取应用配置目录路径 (~/.cc-switch)
|
||||||
|
pub fn get_app_config_dir() -> PathBuf {
|
||||||
|
dirs::home_dir()
|
||||||
|
.expect("无法获取用户主目录")
|
||||||
|
.join(".cc-switch")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取应用配置文件路径
|
||||||
|
pub fn get_app_config_path() -> PathBuf {
|
||||||
|
get_app_config_dir().join("config.json")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 清理供应商名称,确保文件名安全
|
||||||
|
pub fn sanitize_provider_name(name: &str) -> String {
|
||||||
|
name.chars()
|
||||||
|
.map(|c| match c {
|
||||||
|
'<' | '>' | ':' | '"' | '/' | '\\' | '|' | '?' | '*' => '-',
|
||||||
|
_ => c,
|
||||||
|
})
|
||||||
|
.collect::<String>()
|
||||||
|
.to_lowercase()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取供应商配置文件路径
|
||||||
|
pub fn get_provider_config_path(provider_id: &str, provider_name: Option<&str>) -> PathBuf {
|
||||||
|
let base_name = provider_name
|
||||||
|
.map(|name| sanitize_provider_name(name))
|
||||||
|
.unwrap_or_else(|| sanitize_provider_name(provider_id));
|
||||||
|
|
||||||
|
get_claude_config_dir().join(format!("settings-{}.json", base_name))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 读取 JSON 配置文件
|
||||||
|
pub fn read_json_file<T: for<'a> Deserialize<'a>>(path: &Path) -> Result<T, String> {
|
||||||
|
if !path.exists() {
|
||||||
|
return Err(format!("文件不存在: {}", path.display()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let content = fs::read_to_string(path)
|
||||||
|
.map_err(|e| format!("读取文件失败: {}", e))?;
|
||||||
|
|
||||||
|
serde_json::from_str(&content)
|
||||||
|
.map_err(|e| format!("解析 JSON 失败: {}", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 写入 JSON 配置文件
|
||||||
|
pub fn write_json_file<T: Serialize>(path: &Path, data: &T) -> Result<(), String> {
|
||||||
|
// 确保目录存在
|
||||||
|
if let Some(parent) = path.parent() {
|
||||||
|
fs::create_dir_all(parent)
|
||||||
|
.map_err(|e| format!("创建目录失败: {}", e))?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let json = serde_json::to_string_pretty(data)
|
||||||
|
.map_err(|e| format!("序列化 JSON 失败: {}", e))?;
|
||||||
|
|
||||||
|
fs::write(path, json)
|
||||||
|
.map_err(|e| format!("写入文件失败: {}", e))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 复制文件
|
||||||
|
pub fn copy_file(from: &Path, to: &Path) -> Result<(), String> {
|
||||||
|
fs::copy(from, to)
|
||||||
|
.map_err(|e| format!("复制文件失败: {}", e))?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 删除文件
|
||||||
|
pub fn delete_file(path: &Path) -> Result<(), String> {
|
||||||
|
if path.exists() {
|
||||||
|
fs::remove_file(path)
|
||||||
|
.map_err(|e| format!("删除文件失败: {}", e))?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检查 Claude Code 配置状态
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct ConfigStatus {
|
||||||
|
pub exists: bool,
|
||||||
|
pub path: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取 Claude Code 配置状态
|
||||||
|
pub fn get_claude_config_status() -> ConfigStatus {
|
||||||
|
let path = get_claude_settings_path();
|
||||||
|
ConfigStatus {
|
||||||
|
exists: path.exists(),
|
||||||
|
path: path.to_string_lossy().to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 备份配置文件
|
||||||
|
pub fn backup_config(from: &Path, to: &Path) -> Result<(), String> {
|
||||||
|
if from.exists() {
|
||||||
|
copy_file(from, to)?;
|
||||||
|
log::info!("已备份配置文件: {} -> {}", from.display(), to.display());
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 导入当前 Claude Code 配置为默认供应商
|
||||||
|
pub fn import_current_config_as_default() -> Result<Value, String> {
|
||||||
|
let settings_path = get_claude_settings_path();
|
||||||
|
|
||||||
|
if !settings_path.exists() {
|
||||||
|
return Err("Claude Code 配置文件不存在".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取当前配置
|
||||||
|
let settings_config: Value = read_json_file(&settings_path)?;
|
||||||
|
|
||||||
|
// 保存为 default 供应商
|
||||||
|
let default_provider_path = get_provider_config_path("default", Some("default"));
|
||||||
|
write_json_file(&default_provider_path, &settings_config)?;
|
||||||
|
|
||||||
|
log::info!("已导入当前配置为默认供应商");
|
||||||
|
Ok(settings_config)
|
||||||
|
}
|
||||||
180
src-tauri/src/provider.rs
Normal file
180
src-tauri/src/provider.rs
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use serde_json::Value;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::path::Path;
|
||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::config::{
|
||||||
|
copy_file, delete_file, get_provider_config_path, read_json_file, write_json_file,
|
||||||
|
get_claude_settings_path, backup_config, sanitize_provider_name
|
||||||
|
};
|
||||||
|
|
||||||
|
/// 供应商结构体
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Provider {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
#[serde(rename = "settingsConfig")]
|
||||||
|
pub settings_config: Value,
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
|
#[serde(rename = "websiteUrl")]
|
||||||
|
pub website_url: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Provider {
|
||||||
|
/// 创建新的供应商
|
||||||
|
pub fn new(name: String, settings_config: Value, website_url: Option<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
id: Uuid::new_v4().to_string(),
|
||||||
|
name,
|
||||||
|
settings_config,
|
||||||
|
website_url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 从现有ID创建供应商
|
||||||
|
pub fn with_id(id: String, name: String, settings_config: Value, website_url: Option<String>) -> Self {
|
||||||
|
Self {
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
settings_config,
|
||||||
|
website_url,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 供应商管理器
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ProviderManager {
|
||||||
|
pub providers: HashMap<String, Provider>,
|
||||||
|
pub current: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ProviderManager {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
providers: HashMap::new(),
|
||||||
|
current: String::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProviderManager {
|
||||||
|
/// 加载供应商列表
|
||||||
|
pub fn load_from_file(path: &Path) -> Result<Self, String> {
|
||||||
|
if !path.exists() {
|
||||||
|
log::info!("配置文件不存在,创建新的供应商管理器");
|
||||||
|
return Ok(Self::default());
|
||||||
|
}
|
||||||
|
|
||||||
|
read_json_file(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 保存供应商列表
|
||||||
|
pub fn save_to_file(&self, path: &Path) -> Result<(), String> {
|
||||||
|
write_json_file(path, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 添加供应商
|
||||||
|
pub fn add_provider(&mut self, provider: Provider) -> Result<(), String> {
|
||||||
|
// 保存供应商配置到独立文件
|
||||||
|
let config_path = get_provider_config_path(&provider.id, Some(&provider.name));
|
||||||
|
write_json_file(&config_path, &provider.settings_config)?;
|
||||||
|
|
||||||
|
// 添加到管理器
|
||||||
|
self.providers.insert(provider.id.clone(), provider);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 更新供应商
|
||||||
|
pub fn update_provider(&mut self, provider: Provider) -> Result<(), String> {
|
||||||
|
// 检查供应商是否存在
|
||||||
|
if !self.providers.contains_key(&provider.id) {
|
||||||
|
return Err(format!("供应商不存在: {}", provider.id));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果名称改变了,需要处理配置文件
|
||||||
|
if let Some(old_provider) = self.providers.get(&provider.id) {
|
||||||
|
if old_provider.name != provider.name {
|
||||||
|
// 删除旧配置文件
|
||||||
|
let old_config_path = get_provider_config_path(&provider.id, Some(&old_provider.name));
|
||||||
|
delete_file(&old_config_path).ok(); // 忽略删除错误
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存新配置文件
|
||||||
|
let config_path = get_provider_config_path(&provider.id, Some(&provider.name));
|
||||||
|
write_json_file(&config_path, &provider.settings_config)?;
|
||||||
|
|
||||||
|
// 更新管理器
|
||||||
|
self.providers.insert(provider.id.clone(), provider);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 删除供应商
|
||||||
|
pub fn delete_provider(&mut self, provider_id: &str) -> Result<(), String> {
|
||||||
|
// 检查是否为当前供应商
|
||||||
|
if self.current == provider_id {
|
||||||
|
return Err("不能删除当前正在使用的供应商".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取供应商信息
|
||||||
|
let provider = self.providers.get(provider_id)
|
||||||
|
.ok_or_else(|| format!("供应商不存在: {}", provider_id))?;
|
||||||
|
|
||||||
|
// 删除配置文件
|
||||||
|
let config_path = get_provider_config_path(provider_id, Some(&provider.name));
|
||||||
|
delete_file(&config_path)?;
|
||||||
|
|
||||||
|
// 从管理器删除
|
||||||
|
self.providers.remove(provider_id);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 切换供应商
|
||||||
|
pub fn switch_provider(&mut self, provider_id: &str) -> Result<(), String> {
|
||||||
|
// 检查供应商是否存在
|
||||||
|
let provider = self.providers.get(provider_id)
|
||||||
|
.ok_or_else(|| format!("供应商不存在: {}", provider_id))?;
|
||||||
|
|
||||||
|
let settings_path = get_claude_settings_path();
|
||||||
|
let provider_config_path = get_provider_config_path(provider_id, Some(&provider.name));
|
||||||
|
|
||||||
|
// 检查供应商配置文件是否存在
|
||||||
|
if !provider_config_path.exists() {
|
||||||
|
return Err(format!("供应商配置文件不存在: {}", provider_config_path.display()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果当前有配置,先备份到当前供应商
|
||||||
|
if settings_path.exists() && !self.current.is_empty() {
|
||||||
|
if let Some(current_provider) = self.providers.get(&self.current) {
|
||||||
|
let current_provider_path = get_provider_config_path(&self.current, Some(¤t_provider.name));
|
||||||
|
backup_config(&settings_path, ¤t_provider_path)?;
|
||||||
|
log::info!("已备份当前供应商配置: {}", current_provider.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 复制新供应商配置到主配置
|
||||||
|
copy_file(&provider_config_path, &settings_path)?;
|
||||||
|
|
||||||
|
// 更新当前供应商
|
||||||
|
self.current = provider_id.to_string();
|
||||||
|
|
||||||
|
log::info!("成功切换到供应商: {}", provider.name);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取当前供应商
|
||||||
|
pub fn get_current_provider(&self) -> Option<&Provider> {
|
||||||
|
if self.current.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
self.providers.get(&self.current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 获取所有供应商
|
||||||
|
pub fn get_all_providers(&self) -> &HashMap<String, Provider> {
|
||||||
|
&self.providers
|
||||||
|
}
|
||||||
|
}
|
||||||
45
src-tauri/src/store.rs
Normal file
45
src-tauri/src/store.rs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
use std::sync::Mutex;
|
||||||
|
use crate::config::{get_app_config_path, read_json_file, write_json_file};
|
||||||
|
use crate::provider::{Provider, ProviderManager};
|
||||||
|
|
||||||
|
/// 全局应用状态
|
||||||
|
pub struct AppState {
|
||||||
|
pub provider_manager: Mutex<ProviderManager>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AppState {
|
||||||
|
/// 创建新的应用状态
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let config_path = get_app_config_path();
|
||||||
|
let provider_manager = ProviderManager::load_from_file(&config_path)
|
||||||
|
.unwrap_or_else(|e| {
|
||||||
|
log::warn!("加载配置失败: {}, 使用默认配置", e);
|
||||||
|
ProviderManager::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
|
provider_manager: Mutex::new(provider_manager),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 保存配置到文件
|
||||||
|
pub fn save(&self) -> Result<(), String> {
|
||||||
|
let config_path = get_app_config_path();
|
||||||
|
let manager = self.provider_manager.lock()
|
||||||
|
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||||
|
|
||||||
|
manager.save_to_file(&config_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 重新加载配置
|
||||||
|
pub fn reload(&self) -> Result<(), String> {
|
||||||
|
let config_path = get_app_config_path();
|
||||||
|
let new_manager = ProviderManager::load_from_file(&config_path)?;
|
||||||
|
|
||||||
|
let mut manager = self.provider_manager.lock()
|
||||||
|
.map_err(|e| format!("获取锁失败: {}", e))?;
|
||||||
|
|
||||||
|
*manager = new_manager;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user