BREAKING CHANGE: Remove support for legacy app_type/appType parameters.
All Tauri commands now accept only the 'app' parameter (values: "claude" or "codex").
Invalid app values will return localized error messages with allowed values.
This commit addresses code duplication and improves error handling:
- Consolidate AppType parsing into FromStr trait implementation
* Eliminates duplicate parse_app() functions across 3 command modules
* Provides single source of truth for app type validation
* Enables idiomatic Rust .parse::<AppType>() syntax
- Enhance error messages with localization
* Return bilingual error messages (Chinese + English)
* Include list of allowed values in error responses
* Use structured AppError::localized for better categorization
- Add input normalization
* Case-insensitive matching ("CLAUDE" → AppType::Claude)
* Automatic whitespace trimming (" codex \n" → AppType::Codex)
* Improves API robustness against user input variations
- Introduce comprehensive unit tests
* Test valid inputs with case variations
* Test whitespace handling
* Verify error message content and localization
* 100% coverage of from_str logic
- Update documentation
* Add CHANGELOG entry marking breaking change
* Update README with accurate architecture description
* Revise REFACTORING_MASTER_PLAN with migration examples
* Remove all legacy app_type/appType references
Code Quality Metrics:
- Lines removed: 27 (duplicate code)
- Lines added: 52 (including tests and docs)
- Code duplication: 3 → 0 instances
- Test coverage: 0% → 100% for AppType parsing
132 lines
4.1 KiB
Rust
132 lines
4.1 KiB
Rust
#![allow(non_snake_case)]
|
||
|
||
use std::collections::HashMap;
|
||
|
||
use serde::Serialize;
|
||
use tauri::State;
|
||
|
||
use crate::app_config::AppType;
|
||
use crate::claude_mcp;
|
||
use crate::services::McpService;
|
||
use crate::store::AppState;
|
||
|
||
/// 获取 Claude MCP 状态
|
||
#[tauri::command]
|
||
pub async fn get_claude_mcp_status() -> Result<claude_mcp::McpStatus, String> {
|
||
claude_mcp::get_mcp_status().map_err(|e| e.to_string())
|
||
}
|
||
|
||
/// 读取 mcp.json 文本内容
|
||
#[tauri::command]
|
||
pub async fn read_claude_mcp_config() -> Result<Option<String>, String> {
|
||
claude_mcp::read_mcp_json().map_err(|e| e.to_string())
|
||
}
|
||
|
||
/// 新增或更新一个 MCP 服务器条目
|
||
#[tauri::command]
|
||
pub async fn upsert_claude_mcp_server(id: String, spec: serde_json::Value) -> Result<bool, String> {
|
||
claude_mcp::upsert_mcp_server(&id, spec).map_err(|e| e.to_string())
|
||
}
|
||
|
||
/// 删除一个 MCP 服务器条目
|
||
#[tauri::command]
|
||
pub async fn delete_claude_mcp_server(id: String) -> Result<bool, String> {
|
||
claude_mcp::delete_mcp_server(&id).map_err(|e| e.to_string())
|
||
}
|
||
|
||
/// 校验命令是否在 PATH 中可用(不执行)
|
||
#[tauri::command]
|
||
pub async fn validate_mcp_command(cmd: String) -> Result<bool, String> {
|
||
claude_mcp::validate_command_in_path(&cmd).map_err(|e| e.to_string())
|
||
}
|
||
|
||
#[derive(Serialize)]
|
||
pub struct McpConfigResponse {
|
||
pub config_path: String,
|
||
pub servers: HashMap<String, serde_json::Value>,
|
||
}
|
||
|
||
/// 获取 MCP 配置(来自 ~/.cc-switch/config.json)
|
||
use std::str::FromStr;
|
||
|
||
#[tauri::command]
|
||
pub async fn get_mcp_config(
|
||
state: State<'_, AppState>,
|
||
app: String,
|
||
) -> Result<McpConfigResponse, String> {
|
||
let config_path = crate::config::get_app_config_path()
|
||
.to_string_lossy()
|
||
.to_string();
|
||
let app_ty = AppType::from_str(&app).map_err(|e| e.to_string())?;
|
||
let servers = McpService::get_servers(&state, app_ty).map_err(|e| e.to_string())?;
|
||
Ok(McpConfigResponse {
|
||
config_path,
|
||
servers,
|
||
})
|
||
}
|
||
|
||
/// 在 config.json 中新增或更新一个 MCP 服务器定义
|
||
#[tauri::command]
|
||
pub async fn upsert_mcp_server_in_config(
|
||
state: State<'_, AppState>,
|
||
app: String,
|
||
id: String,
|
||
spec: serde_json::Value,
|
||
sync_other_side: Option<bool>,
|
||
) -> Result<bool, String> {
|
||
let app_ty = AppType::from_str(&app).map_err(|e| e.to_string())?;
|
||
McpService::upsert_server(&state, app_ty, &id, spec, sync_other_side.unwrap_or(false))
|
||
.map_err(|e| e.to_string())
|
||
}
|
||
|
||
/// 在 config.json 中删除一个 MCP 服务器定义
|
||
#[tauri::command]
|
||
pub async fn delete_mcp_server_in_config(
|
||
state: State<'_, AppState>,
|
||
app: String,
|
||
id: String,
|
||
) -> Result<bool, String> {
|
||
let app_ty = AppType::from_str(&app).map_err(|e| e.to_string())?;
|
||
McpService::delete_server(&state, app_ty, &id).map_err(|e| e.to_string())
|
||
}
|
||
|
||
/// 设置启用状态并同步到客户端配置
|
||
#[tauri::command]
|
||
pub async fn set_mcp_enabled(
|
||
state: State<'_, AppState>,
|
||
app: String,
|
||
id: String,
|
||
enabled: bool,
|
||
) -> Result<bool, String> {
|
||
let app_ty = AppType::from_str(&app).map_err(|e| e.to_string())?;
|
||
McpService::set_enabled(&state, app_ty, &id, enabled).map_err(|e| e.to_string())
|
||
}
|
||
|
||
/// 手动同步:将启用的 MCP 投影到 ~/.claude.json
|
||
#[tauri::command]
|
||
pub async fn sync_enabled_mcp_to_claude(state: State<'_, AppState>) -> Result<bool, String> {
|
||
McpService::sync_enabled(&state, AppType::Claude)
|
||
.map(|_| true)
|
||
.map_err(|e| e.to_string())
|
||
}
|
||
|
||
/// 手动同步:将启用的 MCP 投影到 ~/.codex/config.toml
|
||
#[tauri::command]
|
||
pub async fn sync_enabled_mcp_to_codex(state: State<'_, AppState>) -> Result<bool, String> {
|
||
McpService::sync_enabled(&state, AppType::Codex)
|
||
.map(|_| true)
|
||
.map_err(|e| e.to_string())
|
||
}
|
||
|
||
/// 从 ~/.claude.json 导入 MCP 定义到 config.json
|
||
#[tauri::command]
|
||
pub async fn import_mcp_from_claude(state: State<'_, AppState>) -> Result<usize, String> {
|
||
McpService::import_from_claude(&state).map_err(|e| e.to_string())
|
||
}
|
||
|
||
/// 从 ~/.codex/config.toml 导入 MCP 定义到 config.json
|
||
#[tauri::command]
|
||
pub async fn import_mcp_from_codex(state: State<'_, AppState>) -> Result<usize, String> {
|
||
McpService::import_from_codex(&state).map_err(|e| e.to_string())
|
||
}
|