refactor(api): simplify app type parameter handling to single required parameter

Replace the previous dual-parameter approach (app_type/app/appType) with a single required `app: String` parameter across all Tauri commands. This change:

- Introduces unified `parse_app()` helper replacing complex `resolve_app_type()` logic
- Updates all backend commands in config, mcp, and provider modules
- Aligns frontend API calls to use consistent `app` parameter naming
- Simplifies MSW test handlers by removing optional parameter handling

This improves API clarity and reduces parameter ambiguity while maintaining backward compatibility through error handling.
This commit is contained in:
Jason
2025-10-30 11:35:14 +08:00
parent 2aec407a2f
commit 931ef7d3dd
8 changed files with 92 additions and 166 deletions

View File

@@ -15,18 +15,17 @@ pub async fn get_claude_config_status() -> Result<ConfigStatus, String> {
}
/// 获取应用配置状态
#[tauri::command]
pub async fn get_config_status(
app_type: Option<AppType>,
app: Option<String>,
appType: Option<String>,
) -> Result<ConfigStatus, String> {
let app = app_type
.or_else(|| app.as_deref().map(|s| s.into()))
.or_else(|| appType.as_deref().map(|s| s.into()))
.unwrap_or(AppType::Claude);
fn parse_app(app: String) -> Result<AppType, String> {
match app.to_lowercase().as_str() {
"claude" => Ok(AppType::Claude),
"codex" => Ok(AppType::Codex),
other => Err(format!("unsupported app: {}", other)),
}
}
match app {
#[tauri::command]
pub async fn get_config_status(app: String) -> Result<ConfigStatus, String> {
match parse_app(app)? {
AppType::Claude => Ok(config::get_claude_config_status()),
AppType::Codex => {
let auth_path = codex_config::get_codex_auth_path();
@@ -48,17 +47,8 @@ pub async fn get_claude_code_config_path() -> Result<String, String> {
/// 获取当前生效的配置目录
#[tauri::command]
pub async fn get_config_dir(
app_type: Option<AppType>,
app: Option<String>,
appType: Option<String>,
) -> Result<String, String> {
let app = app_type
.or_else(|| app.as_deref().map(|s| s.into()))
.or_else(|| appType.as_deref().map(|s| s.into()))
.unwrap_or(AppType::Claude);
let dir = match app {
pub async fn get_config_dir(app: String) -> Result<String, String> {
let dir = match parse_app(app)? {
AppType::Claude => config::get_claude_config_dir(),
AppType::Codex => codex_config::get_codex_config_dir(),
};
@@ -68,18 +58,8 @@ pub async fn get_config_dir(
/// 打开配置文件夹
#[tauri::command]
pub async fn open_config_folder(
handle: AppHandle,
app_type: Option<AppType>,
app: Option<String>,
appType: Option<String>,
) -> Result<bool, String> {
let app_type = app_type
.or_else(|| app.as_deref().map(|s| s.into()))
.or_else(|| appType.as_deref().map(|s| s.into()))
.unwrap_or(AppType::Claude);
let config_dir = match app_type {
pub async fn open_config_folder(handle: AppHandle, app: String) -> Result<bool, String> {
let config_dir = match parse_app(app)? {
AppType::Claude => config::get_claude_config_dir(),
AppType::Codex => codex_config::get_codex_config_dir(),
};

View File

@@ -47,15 +47,23 @@ pub struct McpConfigResponse {
}
/// 获取 MCP 配置(来自 ~/.cc-switch/config.json
fn parse_app(app: String) -> Result<AppType, String> {
match app.to_lowercase().as_str() {
"claude" => Ok(AppType::Claude),
"codex" => Ok(AppType::Codex),
other => Err(format!("unsupported app: {}", other)),
}
}
#[tauri::command]
pub async fn get_mcp_config(
state: State<'_, AppState>,
app: Option<String>,
app: String,
) -> Result<McpConfigResponse, String> {
let config_path = crate::config::get_app_config_path()
.to_string_lossy()
.to_string();
let app_ty = AppType::from(app.as_deref().unwrap_or("claude"));
let app_ty = parse_app(app)?;
let servers = McpService::get_servers(&state, app_ty).map_err(|e| e.to_string())?;
Ok(McpConfigResponse {
config_path,
@@ -67,12 +75,12 @@ pub async fn get_mcp_config(
#[tauri::command]
pub async fn upsert_mcp_server_in_config(
state: State<'_, AppState>,
app: Option<String>,
app: String,
id: String,
spec: serde_json::Value,
sync_other_side: Option<bool>,
) -> Result<bool, String> {
let app_ty = AppType::from(app.as_deref().unwrap_or("claude"));
let app_ty = parse_app(app)?;
McpService::upsert_server(&state, app_ty, &id, spec, sync_other_side.unwrap_or(false))
.map_err(|e| e.to_string())
}
@@ -81,10 +89,10 @@ pub async fn upsert_mcp_server_in_config(
#[tauri::command]
pub async fn delete_mcp_server_in_config(
state: State<'_, AppState>,
app: Option<String>,
app: String,
id: String,
) -> Result<bool, String> {
let app_ty = AppType::from(app.as_deref().unwrap_or("claude"));
let app_ty = parse_app(app)?;
McpService::delete_server(&state, app_ty, &id).map_err(|e| e.to_string())
}
@@ -92,11 +100,11 @@ pub async fn delete_mcp_server_in_config(
#[tauri::command]
pub async fn set_mcp_enabled(
state: State<'_, AppState>,
app: Option<String>,
app: String,
id: String,
enabled: bool,
) -> Result<bool, String> {
let app_ty = AppType::from(app.as_deref().unwrap_or("claude"));
let app_ty = parse_app(app)?;
McpService::set_enabled(&state, app_ty, &id, enabled).map_err(|e| e.to_string())
}

View File

@@ -11,32 +11,11 @@ fn missing_param(param: &str) -> String {
format!("缺少 {} 参数 (Missing {} parameter)", param, param)
}
fn resolve_app_type(app_type: Option<AppType>, app: Option<String>) -> Result<AppType, String> {
match (app_type, app) {
(Some(at), None) => Ok(at),
(None, Some(a)) => match a.to_lowercase().as_str() {
"claude" => Ok(AppType::Claude),
"codex" => Ok(AppType::Codex),
other => Err(format!(
"params.invalid: 无效的 app 值: {} (Invalid app)",
other
)),
},
(Some(at), Some(a)) => {
let a_norm = a.to_lowercase();
let at_norm = at.as_str().to_string();
if a_norm == at_norm {
// 接受但提示:建议仅传 app
log::warn!("params.deprecated: 同时传递 app 与 app_type建议仅使用 app");
Ok(at)
} else {
Err(format!(
"params.conflict: app 与 app_type 冲突 (app={}, app_type={})",
a_norm, at_norm
))
}
}
(None, None) => Err(missing_param("app")),
fn parse_app(app: String) -> Result<AppType, String> {
match app.to_lowercase().as_str() {
"claude" => Ok(AppType::Claude),
"codex" => Ok(AppType::Codex),
other => Err(format!("unsupported app: {}", other)),
}
}
@@ -44,10 +23,9 @@ fn resolve_app_type(app_type: Option<AppType>, app: Option<String>) -> Result<Ap
#[tauri::command]
pub fn get_providers(
state: State<'_, AppState>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
) -> Result<HashMap<String, Provider>, String> {
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
ProviderService::list(state.inner(), app_type).map_err(|e| e.to_string())
}
@@ -55,10 +33,9 @@ pub fn get_providers(
#[tauri::command]
pub fn get_current_provider(
state: State<'_, AppState>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
) -> Result<String, String> {
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
ProviderService::current(state.inner(), app_type).map_err(|e| e.to_string())
}
@@ -66,11 +43,10 @@ pub fn get_current_provider(
#[tauri::command]
pub fn add_provider(
state: State<'_, AppState>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
provider: Provider,
) -> Result<bool, String> {
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
ProviderService::add(state.inner(), app_type, provider).map_err(|e| e.to_string())
}
@@ -78,11 +54,10 @@ pub fn add_provider(
#[tauri::command]
pub fn update_provider(
state: State<'_, AppState>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
provider: Provider,
) -> Result<bool, String> {
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
ProviderService::update(state.inner(), app_type, provider).map_err(|e| e.to_string())
}
@@ -90,11 +65,10 @@ pub fn update_provider(
#[tauri::command]
pub fn delete_provider(
state: State<'_, AppState>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
id: String,
) -> Result<bool, String> {
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
ProviderService::delete(state.inner(), app_type, &id)
.map(|_| true)
.map_err(|e| e.to_string())
@@ -117,11 +91,10 @@ pub fn switch_provider_test_hook(
#[tauri::command]
pub fn switch_provider(
state: State<'_, AppState>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
id: String,
) -> Result<bool, String> {
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
switch_provider_internal(&state, app_type, &id)
.map(|_| true)
.map_err(|e| e.to_string())
@@ -143,10 +116,9 @@ pub fn import_default_config_test_hook(
#[tauri::command]
pub fn import_default_config(
state: State<'_, AppState>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
) -> Result<bool, String> {
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
import_default_config_internal(&state, app_type)
.map(|_| true)
.map_err(Into::into)
@@ -157,11 +129,10 @@ pub fn import_default_config(
pub async fn query_provider_usage(
state: State<'_, AppState>,
provider_id: Option<String>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
) -> Result<crate::provider::UsageResult, String> {
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
ProviderService::query_usage(state.inner(), app_type, &provider_id)
.await
.map_err(|e| e.to_string())
@@ -169,9 +140,8 @@ pub async fn query_provider_usage(
/// 读取当前生效的配置内容
#[tauri::command]
pub fn read_live_provider_settings(app_type: Option<AppType>) -> Result<serde_json::Value, String> {
let app_type = app_type.unwrap_or(AppType::Claude);
pub fn read_live_provider_settings(app: String) -> Result<serde_json::Value, String> {
let app_type = parse_app(app)?;
ProviderService::read_live_settings(app_type).map_err(|e| e.to_string())
}
@@ -190,11 +160,10 @@ pub async fn test_api_endpoints(
#[tauri::command]
pub fn get_custom_endpoints(
state: State<'_, AppState>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
provider_id: Option<String>,
) -> Result<Vec<crate::settings::CustomEndpoint>, String> {
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
ProviderService::get_custom_endpoints(state.inner(), app_type, &provider_id)
.map_err(|e| e.to_string())
@@ -204,12 +173,11 @@ pub fn get_custom_endpoints(
#[tauri::command]
pub fn add_custom_endpoint(
state: State<'_, AppState>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
provider_id: Option<String>,
url: String,
) -> Result<(), String> {
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
ProviderService::add_custom_endpoint(state.inner(), app_type, &provider_id, url)
.map_err(|e| e.to_string())
@@ -219,12 +187,11 @@ pub fn add_custom_endpoint(
#[tauri::command]
pub fn remove_custom_endpoint(
state: State<'_, AppState>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
provider_id: Option<String>,
url: String,
) -> Result<(), String> {
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
ProviderService::remove_custom_endpoint(state.inner(), app_type, &provider_id, url)
.map_err(|e| e.to_string())
@@ -234,12 +201,11 @@ pub fn remove_custom_endpoint(
#[tauri::command]
pub fn update_endpoint_last_used(
state: State<'_, AppState>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
provider_id: Option<String>,
url: String,
) -> Result<(), String> {
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
ProviderService::update_endpoint_last_used(state.inner(), app_type, &provider_id, url)
.map_err(|e| e.to_string())
@@ -249,10 +215,9 @@ pub fn update_endpoint_last_used(
#[tauri::command]
pub fn update_providers_sort_order(
state: State<'_, AppState>,
app_type: Option<AppType>,
app: Option<String>,
app: String,
updates: Vec<ProviderSortUpdate>,
) -> Result<bool, String> {
let app_type = resolve_app_type(app_type, app)?;
let app_type = parse_app(app)?;
ProviderService::update_sort_order(state.inner(), app_type, updates).map_err(|e| e.to_string())
}

View File

@@ -267,12 +267,7 @@ fn switch_provider_internal(
let app_type_str = app_type.as_str().to_string();
let provider_id_clone = provider_id.clone();
crate::commands::switch_provider(
app_state.clone(),
Some(app_type),
Some(app_type_str.clone()),
provider_id,
)
crate::commands::switch_provider(app_state.clone(), app_type_str.clone(), provider_id)
.map_err(AppError::Message)?;
// 切换成功后重新创建托盘菜单