diff --git a/src-tauri/src/auto_launch.rs b/src-tauri/src/auto_launch.rs index bdb7748a..26c1d5c3 100644 --- a/src-tauri/src/auto_launch.rs +++ b/src-tauri/src/auto_launch.rs @@ -1,16 +1,36 @@ use crate::error::AppError; use auto_launch::{AutoLaunch, AutoLaunchBuilder}; +/// 获取 macOS 上的 .app bundle 路径 +/// 将 `/path/to/CC Switch.app/Contents/MacOS/CC Switch` 转换为 `/path/to/CC Switch.app` +#[cfg(target_os = "macos")] +fn get_macos_app_bundle_path(exe_path: &std::path::Path) -> Option { + let path_str = exe_path.to_string_lossy(); + // 查找 .app/Contents/MacOS/ 模式 + if let Some(app_pos) = path_str.find(".app/Contents/MacOS/") { + let app_bundle_end = app_pos + 4; // ".app" 的结束位置 + Some(std::path::PathBuf::from(&path_str[..app_bundle_end])) + } else { + None + } +} + /// 初始化 AutoLaunch 实例 fn get_auto_launch() -> Result { let app_name = "CC Switch"; - let app_path = + let exe_path = std::env::current_exe().map_err(|e| AppError::Message(format!("无法获取应用路径: {e}")))?; + // macOS 需要使用 .app bundle 路径,否则 AppleScript login item 会打开终端 + #[cfg(target_os = "macos")] + let app_path = get_macos_app_bundle_path(&exe_path).unwrap_or(exe_path); + + #[cfg(not(target_os = "macos"))] + let app_path = exe_path; + // 使用 AutoLaunchBuilder 消除平台差异 - // Windows/Linux: new() 接受 3 参数 - // macOS: new() 接受 4 参数(含 hidden 参数) - // Builder 模式自动处理这些差异 + // macOS: 使用 AppleScript 方式(默认),需要 .app bundle 路径 + // Windows/Linux: 使用注册表/XDG autostart let auto_launch = AutoLaunchBuilder::new() .set_app_name(app_name) .set_app_path(&app_path.to_string_lossy()) @@ -47,3 +67,49 @@ pub fn is_auto_launch_enabled() -> Result { .is_enabled() .map_err(|e| AppError::Message(format!("检查开机自启状态失败: {e}"))) } + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(target_os = "macos")] + #[test] + fn test_get_macos_app_bundle_path_valid() { + let exe_path = + std::path::Path::new("/Applications/CC Switch.app/Contents/MacOS/CC Switch"); + let result = get_macos_app_bundle_path(exe_path); + assert_eq!( + result, + Some(std::path::PathBuf::from("/Applications/CC Switch.app")) + ); + } + + #[cfg(target_os = "macos")] + #[test] + fn test_get_macos_app_bundle_path_with_spaces() { + let exe_path = + std::path::Path::new("/Users/test/My Apps/CC Switch.app/Contents/MacOS/CC Switch"); + let result = get_macos_app_bundle_path(exe_path); + assert_eq!( + result, + Some(std::path::PathBuf::from("/Users/test/My Apps/CC Switch.app")) + ); + } + + #[cfg(target_os = "macos")] + #[test] + fn test_get_macos_app_bundle_path_not_in_bundle() { + let exe_path = std::path::Path::new("/usr/local/bin/cc-switch"); + let result = get_macos_app_bundle_path(exe_path); + assert_eq!(result, None); + } + + #[cfg(target_os = "macos")] + #[test] + fn test_get_macos_app_bundle_path_dev_build() { + // 开发环境下的路径通常不在 .app bundle 内 + let exe_path = std::path::Path::new("/Users/dev/project/target/debug/cc-switch"); + let result = get_macos_app_bundle_path(exe_path); + assert_eq!(result, None); + } +}