refactor(providers): add flexible app type resolution with dual parameter support
Add `resolve_app_type` helper to support both enum and string-based app type parameters across all provider commands. This change: - Eliminates implicit default to Claude (previously used `unwrap_or`) - Supports two parameter forms: `app_type` (enum, priority 1) and `app` (string, priority 2) - Provides explicit error handling when both parameters are missing - Updates all 14 provider command functions with consistent parameter validation - Fixes tray menu provider switching to pass the new `app` parameter This dual-parameter approach maintains backward compatibility while enabling future CLI tool integration and more flexible API usage patterns. Technical details: - Priority order: `app_type` enum > `app` string > error - Invalid `app` strings now return errors instead of defaulting - All existing tests pass (45/45)
This commit is contained in:
@@ -11,14 +11,43 @@ fn missing_param(param: &str) -> String {
|
|||||||
format!("缺少 {} 参数 (Missing {} parameter)", param, param)
|
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")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// 获取所有供应商
|
/// 获取所有供应商
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub fn get_providers(
|
pub fn get_providers(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
) -> Result<HashMap<String, Provider>, String> {
|
) -> Result<HashMap<String, Provider>, String> {
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
|
|
||||||
ProviderService::list(state.inner(), app_type).map_err(|e| e.to_string())
|
ProviderService::list(state.inner(), app_type).map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -27,9 +56,9 @@ pub fn get_providers(
|
|||||||
pub fn get_current_provider(
|
pub fn get_current_provider(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
|
|
||||||
ProviderService::current(state.inner(), app_type).map_err(|e| e.to_string())
|
ProviderService::current(state.inner(), app_type).map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,10 +67,10 @@ pub fn get_current_provider(
|
|||||||
pub fn add_provider(
|
pub fn add_provider(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
provider: Provider,
|
provider: Provider,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
|
|
||||||
ProviderService::add(state.inner(), app_type, provider).map_err(|e| e.to_string())
|
ProviderService::add(state.inner(), app_type, provider).map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,10 +79,10 @@ pub fn add_provider(
|
|||||||
pub fn update_provider(
|
pub fn update_provider(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
provider: Provider,
|
provider: Provider,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
|
|
||||||
ProviderService::update(state.inner(), app_type, provider).map_err(|e| e.to_string())
|
ProviderService::update(state.inner(), app_type, provider).map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,10 +91,10 @@ pub fn update_provider(
|
|||||||
pub fn delete_provider(
|
pub fn delete_provider(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
id: String,
|
id: String,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
|
|
||||||
ProviderService::delete(state.inner(), app_type, &id)
|
ProviderService::delete(state.inner(), app_type, &id)
|
||||||
.map(|_| true)
|
.map(|_| true)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
@@ -89,10 +118,10 @@ pub fn switch_provider_test_hook(
|
|||||||
pub fn switch_provider(
|
pub fn switch_provider(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
id: String,
|
id: String,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
|
|
||||||
switch_provider_internal(&state, app_type, &id)
|
switch_provider_internal(&state, app_type, &id)
|
||||||
.map(|_| true)
|
.map(|_| true)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
@@ -115,9 +144,9 @@ pub fn import_default_config_test_hook(
|
|||||||
pub fn import_default_config(
|
pub fn import_default_config(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
|
|
||||||
import_default_config_internal(&state, app_type)
|
import_default_config_internal(&state, app_type)
|
||||||
.map(|_| true)
|
.map(|_| true)
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
@@ -129,11 +158,10 @@ pub async fn query_provider_usage(
|
|||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
provider_id: Option<String>,
|
provider_id: Option<String>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
) -> Result<crate::provider::UsageResult, String> {
|
) -> Result<crate::provider::UsageResult, String> {
|
||||||
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
|
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
|
||||||
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
|
||||||
|
|
||||||
ProviderService::query_usage(state.inner(), app_type, &provider_id)
|
ProviderService::query_usage(state.inner(), app_type, &provider_id)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
@@ -163,9 +191,10 @@ pub async fn test_api_endpoints(
|
|||||||
pub fn get_custom_endpoints(
|
pub fn get_custom_endpoints(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
provider_id: Option<String>,
|
provider_id: Option<String>,
|
||||||
) -> Result<Vec<crate::settings::CustomEndpoint>, String> {
|
) -> Result<Vec<crate::settings::CustomEndpoint>, String> {
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
|
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
|
||||||
ProviderService::get_custom_endpoints(state.inner(), app_type, &provider_id)
|
ProviderService::get_custom_endpoints(state.inner(), app_type, &provider_id)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
@@ -176,10 +205,11 @@ pub fn get_custom_endpoints(
|
|||||||
pub fn add_custom_endpoint(
|
pub fn add_custom_endpoint(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
provider_id: Option<String>,
|
provider_id: Option<String>,
|
||||||
url: String,
|
url: String,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
|
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
|
||||||
ProviderService::add_custom_endpoint(state.inner(), app_type, &provider_id, url)
|
ProviderService::add_custom_endpoint(state.inner(), app_type, &provider_id, url)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
@@ -190,10 +220,11 @@ pub fn add_custom_endpoint(
|
|||||||
pub fn remove_custom_endpoint(
|
pub fn remove_custom_endpoint(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
provider_id: Option<String>,
|
provider_id: Option<String>,
|
||||||
url: String,
|
url: String,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
|
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
|
||||||
ProviderService::remove_custom_endpoint(state.inner(), app_type, &provider_id, url)
|
ProviderService::remove_custom_endpoint(state.inner(), app_type, &provider_id, url)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
@@ -204,10 +235,11 @@ pub fn remove_custom_endpoint(
|
|||||||
pub fn update_endpoint_last_used(
|
pub fn update_endpoint_last_used(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
provider_id: Option<String>,
|
provider_id: Option<String>,
|
||||||
url: String,
|
url: String,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
|
let provider_id = provider_id.ok_or_else(|| missing_param("providerId"))?;
|
||||||
ProviderService::update_endpoint_last_used(state.inner(), app_type, &provider_id, url)
|
ProviderService::update_endpoint_last_used(state.inner(), app_type, &provider_id, url)
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
@@ -218,9 +250,9 @@ pub fn update_endpoint_last_used(
|
|||||||
pub fn update_providers_sort_order(
|
pub fn update_providers_sort_order(
|
||||||
state: State<'_, AppState>,
|
state: State<'_, AppState>,
|
||||||
app_type: Option<AppType>,
|
app_type: Option<AppType>,
|
||||||
|
app: Option<String>,
|
||||||
updates: Vec<ProviderSortUpdate>,
|
updates: Vec<ProviderSortUpdate>,
|
||||||
) -> Result<bool, String> {
|
) -> Result<bool, String> {
|
||||||
let app_type = app_type.unwrap_or(AppType::Claude);
|
let app_type = resolve_app_type(app_type, app)?;
|
||||||
|
|
||||||
ProviderService::update_sort_order(state.inner(), app_type, updates).map_err(|e| e.to_string())
|
ProviderService::update_sort_order(state.inner(), app_type, updates).map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -267,7 +267,12 @@ fn switch_provider_internal(
|
|||||||
let app_type_str = app_type.as_str().to_string();
|
let app_type_str = app_type.as_str().to_string();
|
||||||
let provider_id_clone = provider_id.clone();
|
let provider_id_clone = provider_id.clone();
|
||||||
|
|
||||||
crate::commands::switch_provider(app_state.clone(), Some(app_type), provider_id)
|
crate::commands::switch_provider(
|
||||||
|
app_state.clone(),
|
||||||
|
Some(app_type),
|
||||||
|
Some(app_type_str.clone()),
|
||||||
|
provider_id,
|
||||||
|
)
|
||||||
.map_err(AppError::Message)?;
|
.map_err(AppError::Message)?;
|
||||||
|
|
||||||
// 切换成功后重新创建托盘菜单
|
// 切换成功后重新创建托盘菜单
|
||||||
|
|||||||
@@ -15,31 +15,31 @@ export interface ProviderSwitchEvent {
|
|||||||
|
|
||||||
export const providersApi = {
|
export const providersApi = {
|
||||||
async getAll(appType: AppType): Promise<Record<string, Provider>> {
|
async getAll(appType: AppType): Promise<Record<string, Provider>> {
|
||||||
return await invoke("get_providers", { app_type: appType });
|
return await invoke("get_providers", { app: appType });
|
||||||
},
|
},
|
||||||
|
|
||||||
async getCurrent(appType: AppType): Promise<string> {
|
async getCurrent(appType: AppType): Promise<string> {
|
||||||
return await invoke("get_current_provider", { app_type: appType });
|
return await invoke("get_current_provider", { app: appType });
|
||||||
},
|
},
|
||||||
|
|
||||||
async add(provider: Provider, appType: AppType): Promise<boolean> {
|
async add(provider: Provider, appType: AppType): Promise<boolean> {
|
||||||
return await invoke("add_provider", { provider, app_type: appType });
|
return await invoke("add_provider", { provider, app: appType });
|
||||||
},
|
},
|
||||||
|
|
||||||
async update(provider: Provider, appType: AppType): Promise<boolean> {
|
async update(provider: Provider, appType: AppType): Promise<boolean> {
|
||||||
return await invoke("update_provider", { provider, app_type: appType });
|
return await invoke("update_provider", { provider, app: appType });
|
||||||
},
|
},
|
||||||
|
|
||||||
async delete(id: string, appType: AppType): Promise<boolean> {
|
async delete(id: string, appType: AppType): Promise<boolean> {
|
||||||
return await invoke("delete_provider", { id, app_type: appType });
|
return await invoke("delete_provider", { id, app: appType });
|
||||||
},
|
},
|
||||||
|
|
||||||
async switch(id: string, appType: AppType): Promise<boolean> {
|
async switch(id: string, appType: AppType): Promise<boolean> {
|
||||||
return await invoke("switch_provider", { id, app_type: appType });
|
return await invoke("switch_provider", { id, app: appType });
|
||||||
},
|
},
|
||||||
|
|
||||||
async importDefault(appType: AppType): Promise<boolean> {
|
async importDefault(appType: AppType): Promise<boolean> {
|
||||||
return await invoke("import_default_config", { app_type: appType });
|
return await invoke("import_default_config", { app: appType });
|
||||||
},
|
},
|
||||||
|
|
||||||
async updateTrayMenu(): Promise<boolean> {
|
async updateTrayMenu(): Promise<boolean> {
|
||||||
@@ -50,10 +50,7 @@ export const providersApi = {
|
|||||||
updates: ProviderSortUpdate[],
|
updates: ProviderSortUpdate[],
|
||||||
appType: AppType,
|
appType: AppType,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
return await invoke("update_providers_sort_order", {
|
return await invoke("update_providers_sort_order", { updates, app: appType });
|
||||||
updates,
|
|
||||||
app_type: appType,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
async onSwitched(
|
async onSwitched(
|
||||||
|
|||||||
Reference in New Issue
Block a user