From 06aad52e0f1c2d7dabea43c97a9eb367efae6094 Mon Sep 17 00:00:00 2001 From: "954801926@qq.com" Date: Thu, 30 Jul 2026 00:21:54 +0800 Subject: [PATCH] feat(update): TDD platform-aware asset picker + tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 抽取 pick_asset_for_os(assets, os) 纯函数(可测) - pick_asset_for_current_os 包裹运行时 OS - update_temp_filename 按 OS 决定后缀 - 6 个单元测覆盖 win/mac/linux + missing/empty/unknown --- src-tauri/src/commands.rs | 99 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/src-tauri/src/commands.rs b/src-tauri/src/commands.rs index e144bff..e19d6ab 100644 --- a/src-tauri/src/commands.rs +++ b/src-tauri/src/commands.rs @@ -868,4 +868,103 @@ pub fn open_installer(app: tauri::AppHandle, path: String) -> Result<(), String> app.opener() .open_path(&path, None::<&str>) .map_err(|e| e.to_string()) +} + +/// Pick the GitHub release asset matching the given OS by file extension. +/// Preference for Linux is AppImage (portable, no install). If the preferred +/// extension is not present, returns None so the UI can fall back to +/// "open release page" instead of guessing a less-preferred format. +fn pick_asset_for_os(assets: &[serde_json::Value], os: &str) -> Option { + let target_ext = match os { + "macos" => "dmg", + "linux" => "AppImage", + "windows" => "msi", + _ => return None, + }; + assets + .iter() + .find(|a| { + a.get("name") + .and_then(|n| n.as_str()) + .map(|n| n.ends_with(target_ext)) + .unwrap_or(false) + }) + .and_then(|a| a.get("browser_download_url")) + .and_then(|v| v.as_str()) + .map(|s| s.to_string()) +} + +/// Pick the GitHub release asset for the current OS (runtime target). +fn pick_asset_for_current_os(assets: &[serde_json::Value]) -> Option { + pick_asset_for_os(assets, std::env::consts::OS) +} + +/// Build the platform-specific temp filename for the downloaded installer. +fn update_temp_filename() -> String { + let ext = match std::env::consts::OS { + "macos" => "dmg", + "linux" => "AppImage", + "windows" => "msi", + _ => "bin", + }; + format!("KimiSwitch_update.{ext}") +} + +#[cfg(test)] +mod asset_picker_tests { + use super::*; + use serde_json::json; + + fn assets(names: &[&str]) -> Vec { + names + .iter() + .map(|n| { + json!({ + "name": n, + "browser_download_url": format!("https://example.com/{n}"), + }) + }) + .collect() + } + + #[test] + fn windows_picks_msi() { + let assets = assets(&["KimiSwitch_0.7.0_x64_en-US.msi", "KimiSwitch_0.7.0_aarch64.dmg", "KimiSwitch_0.7.0_amd64.AppImage"]); + let url = pick_asset_for_os(&assets, "windows"); + assert_eq!(url.as_deref(), Some("https://example.com/KimiSwitch_0.7.0_x64_en-US.msi")); + } + + #[test] + fn macos_picks_dmg() { + let assets = assets(&["KimiSwitch_0.7.0_x64_en-US.msi", "KimiSwitch_0.7.0_aarch64.dmg", "KimiSwitch_0.7.0_amd64.AppImage"]); + let url = pick_asset_for_os(&assets, "macos"); + assert_eq!(url.as_deref(), Some("https://example.com/KimiSwitch_0.7.0_aarch64.dmg")); + } + + #[test] + fn linux_picks_appimage() { + let assets = assets(&["KimiSwitch_0.7.0_x64_en-US.msi", "KimiSwitch_0.7.0_aarch64.dmg", "KimiSwitch_0.7.0_amd64.AppImage"]); + let url = pick_asset_for_os(&assets, "linux"); + assert_eq!(url.as_deref(), Some("https://example.com/KimiSwitch_0.7.0_amd64.AppImage")); + } + + #[test] + fn missing_target_returns_none() { + let assets = assets(&["KimiSwitch_0.7.0_x64_en-US.msi"]); + let url = pick_asset_for_os(&assets, "macos"); + assert_eq!(url, None); + } + + #[test] + fn empty_assets_returns_none() { + let url = pick_asset_for_os(&[], "linux"); + assert_eq!(url, None); + } + + #[test] + fn unknown_os_returns_none() { + let assets = assets(&["KimiSwitch_0.7.0_x64_en-US.msi"]); + let url = pick_asset_for_os(&assets, "freebsd"); + assert_eq!(url, None); + } } \ No newline at end of file