use crate::db::pool; use rusqlite::params; use std::path::Path; use std::process::Command; /// 将 UNC WSL 路径转换为 Linux 路径 /// \\wsl.localhost\Ubuntu\home\user\proj → /home/user/proj /// \\wsl$\Ubuntu\home\user\proj → /home/user/proj /// /home/user/proj → /home/user/proj(直接返回) fn unc_to_linux(path: &str) -> String { for prefix in &["\\\\wsl.localhost\\", "\\\\wsl$\\"] { if path.to_lowercase().starts_with(&prefix.to_lowercase()) { let rest = &path[prefix.len()..]; // 去掉 distro 名(第一段),剩余部分转 / let after_distro = rest.splitn(2, '\\').nth(1).unwrap_or(""); return format!("/{}", after_distro.replace('\\', "/")); } } // 已经是 Linux 路径,直接返回 path.to_string() } fn get_setting(key: &str) -> Option { let conn = pool().get().ok()?; // 先查全局 settings 表 if let Ok(val) = conn.query_row( "SELECT value FROM settings WHERE key = ?1", params![key], |row| row.get::<_, String>(0), ) { if !val.is_empty() { return Some(val); } } // 兼容旧数据:回退查 space_settings(任意空间取第一条) conn.query_row( "SELECT value FROM space_settings WHERE key = ?1 LIMIT 1", params![key], |row| row.get::<_, String>(0), ).ok() } #[tauri::command] pub fn open_editor(project_id: String, env: String, editor_path: String) -> Result { let conn = pool().get().map_err(|e| e.to_string())?; let (wsl_path, win_path): (Option, Option) = conn .query_row( "SELECT wsl_path, win_path FROM project_workspaces WHERE id = ?1", params![project_id], |row| Ok((row.get(0)?, row.get(1)?)), ) .map_err(|_| format!("Project not found: {project_id}"))?; let wsl_distro = get_setting("wsl_distro").unwrap_or_else(|| "Ubuntu".into()); // WSL 环境:按 wsl_open_mode 配置自适应启动 // wsl_internal (默认): wsl -d -- // remote_flag : --remote wsl+ // unc_path : (直接用 UNC 路径,不走 Remote) if env == "wsl" { let path = wsl_path.ok_or("WSL path not configured")?; let open_mode = get_setting("wsl_open_mode") .unwrap_or_else(|| "wsl_internal".into()); if open_mode == "wsl_internal" { // 直接调用 wsl 进程,用参数数组传路径,避免 cmd /c 的引号转义问题 let linux_path = unc_to_linux(&path); let cmd_name = std::path::Path::new(&editor_path) .file_stem() .and_then(|s| s.to_str()) .map(|s| s.to_lowercase()) .unwrap_or_else(|| editor_path.clone()); Command::new("wsl") .args(["-d", &wsl_distro, "--", &cmd_name, &linux_path]) .spawn() .map_err(|e| format!("无法启动编辑器: {e}"))?; return Ok(format!("wsl -d {} -- {} {}", wsl_distro, cmd_name, linux_path)); } let cmd_str = match open_mode.as_str() { "remote_flag" => { let linux_path = unc_to_linux(&path); format!("\"{}\" --remote wsl+{} \"{}\"", editor_path, wsl_distro, linux_path) } _ => { // unc_path:直接用原始路径,不走 Remote format!("\"{}\" \"{}\"", editor_path, path) } }; Command::new("cmd") .args(["/c", &cmd_str]) .spawn() .map_err(|e| format!("无法启动编辑器: {e}"))?; return Ok(cmd_str); } let args: Vec = match env.as_str() { "win" => { let path = win_path.ok_or("Windows path not configured")?; vec![path] } _ => return Err(format!("Unknown env: {env}")), }; // Windows 上编辑器可能是 .cmd 脚本,通过 cmd /c 让 shell 解析 #[cfg(target_os = "windows")] let spawn_result = Command::new("cmd") .args(["/c", &editor_path]) .args(&args) .spawn(); #[cfg(not(target_os = "windows"))] let spawn_result = Command::new(&editor_path).args(&args).spawn(); spawn_result.map_err(|e| { format!("无法启动编辑器 \"{editor_path}\": {e}\n请在设置中检查编辑器路径是否正确") })?; Ok(format!("{editor_path} {}", args.join(" "))) } #[tauri::command] pub fn open_terminal(project_id: String, env: String) -> Result { let conn = pool().get().map_err(|e| e.to_string())?; let (wsl_path, win_path): (Option, Option) = conn .query_row( "SELECT wsl_path, win_path FROM project_workspaces WHERE id = ?1", params![project_id], |row| Ok((row.get(0)?, row.get(1)?)), ) .map_err(|_| format!("Project not found: {project_id}"))?; let wsl_distro = get_setting("wsl_distro").unwrap_or_else(|| "Ubuntu".into()); let args: Vec = match env.as_str() { "wsl" => { let path = wsl_path.ok_or("WSL path not configured")?; vec!["-p".into(), wsl_distro, "--".into(), "wsl".into(), "--cd".into(), path] } "win" => { let path = win_path.ok_or("Windows path not configured")?; if Path::new(&path).exists() { vec!["-p".into(), "PowerShell".into(), "-d".into(), path] } else { vec!["-p".into(), "PowerShell".into()] } } _ => return Err(format!("Unknown env: {env}")), }; #[cfg(target_os = "windows")] let term_result = Command::new("cmd").args(["/c", "wt"]).args(&args).spawn(); #[cfg(not(target_os = "windows"))] let term_result = Command::new("wt").args(&args).spawn(); term_result.map_err(|e| format!("无法启动终端: {e}"))?; Ok(format!("wt {}", args.join(" "))) }