use crate::db::pool; use rusqlite::params; use serde::{Deserialize, Serialize}; use std::process::Command as StdCommand; // ── 数据结构 ────────────────────────────────────────────────────────────────── #[derive(Debug, Serialize)] pub struct ProjectTool { pub id: String, pub project_id: String, pub name: String, pub icon: String, pub exe_path: String, pub args: Option, pub sort_order: i32, } #[derive(Debug, Deserialize)] pub struct ProjectToolInput { pub name: String, pub icon: String, pub exe_path: String, pub args: Option, pub sort_order: Option, } // ── Tauri 命令 ──────────────────────────────────────────────────────────────── /// 获取某个项目的所有工具 #[tauri::command] pub fn get_project_tools(project_id: String) -> Result, String> { let conn = pool().get().map_err(|e| e.to_string())?; let mut stmt = conn .prepare( "SELECT id, project_id, name, icon, exe_path, args, sort_order FROM project_tools WHERE project_id = ?1 ORDER BY sort_order ASC, rowid ASC", ) .map_err(|e| e.to_string())?; let rows = stmt .query_map(params![project_id], |row| { Ok(ProjectTool { id: row.get(0)?, project_id: row.get(1)?, name: row.get(2)?, icon: row.get(3)?, exe_path: row.get(4)?, args: row.get(5)?, sort_order: row.get(6)?, }) }) .map_err(|e| e.to_string())? .collect::, _>>() .map_err(|e| e.to_string()); rows } /// 添加工具到项目 #[tauri::command] pub fn add_project_tool(project_id: String, input: ProjectToolInput) -> Result { let conn = pool().get().map_err(|e| e.to_string())?; let id = uuid::Uuid::new_v4().to_string(); conn.execute( "INSERT INTO project_tools (id, project_id, name, icon, exe_path, args, sort_order) VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)", params![ id, project_id, input.name, input.icon, input.exe_path, input.args, input.sort_order.unwrap_or(0), ], ) .map_err(|e| e.to_string())?; Ok(id) } /// 更新工具信息 #[tauri::command] pub fn update_project_tool(id: String, input: ProjectToolInput) -> Result<(), String> { let conn = pool().get().map_err(|e| e.to_string())?; conn.execute( "UPDATE project_tools SET name = ?1, icon = ?2, exe_path = ?3, args = ?4, sort_order = ?5 WHERE id = ?6", params![ input.name, input.icon, input.exe_path, input.args, input.sort_order.unwrap_or(0), id, ], ) .map_err(|e| e.to_string())?; Ok(()) } /// 删除项目工具 #[tauri::command] pub fn remove_project_tool(id: String) -> Result<(), String> { let conn = pool().get().map_err(|e| e.to_string())?; conn.execute("DELETE FROM project_tools WHERE id = ?1", params![id]) .map_err(|e| e.to_string())?; Ok(()) } // ── 全局快启命令(写入 global_tools 表,无项目 FK 约束)──────────────────────── #[tauri::command] pub fn get_global_tools() -> Result, String> { let conn = pool().get().map_err(|e| e.to_string())?; let mut stmt = conn .prepare( "SELECT id, name, icon, exe_path, args, sort_order FROM global_tools ORDER BY sort_order ASC, rowid ASC", ) .map_err(|e| e.to_string())?; let rows = stmt .query_map([], |row| { Ok(ProjectTool { id: row.get(0)?, project_id: "__global__".to_string(), name: row.get(1)?, icon: row.get(2)?, exe_path: row.get(3)?, args: row.get(4)?, sort_order: row.get(5)?, }) }) .map_err(|e| e.to_string())? .collect::, _>>() .map_err(|e| e.to_string()); rows } #[tauri::command] pub fn add_global_tool(input: ProjectToolInput) -> Result { let conn = pool().get().map_err(|e| e.to_string())?; let id = uuid::Uuid::new_v4().to_string(); conn.execute( "INSERT INTO global_tools (id, name, icon, exe_path, args, sort_order) VALUES (?1, ?2, ?3, ?4, ?5, ?6)", params![ id, input.name, input.icon, input.exe_path, input.args, input.sort_order.unwrap_or(0), ], ) .map_err(|e| e.to_string())?; Ok(id) } #[tauri::command] pub fn update_global_tool(id: String, input: ProjectToolInput) -> Result<(), String> { let conn = pool().get().map_err(|e| e.to_string())?; conn.execute( "UPDATE global_tools SET name = ?1, icon = ?2, exe_path = ?3, args = ?4, sort_order = ?5 WHERE id = ?6", params![ input.name, input.icon, input.exe_path, input.args, input.sort_order.unwrap_or(0), id, ], ) .map_err(|e| e.to_string())?; Ok(()) } #[tauri::command] pub fn remove_global_tool(id: String) -> Result<(), String> { let conn = pool().get().map_err(|e| e.to_string())?; conn.execute("DELETE FROM global_tools WHERE id = ?1", params![id]) .map_err(|e| e.to_string())?; Ok(()) } /// 启动工具(通用:exe 路径 + 可选参数) #[tauri::command] pub fn launch_tool(exe_path: String, args: Option) -> Result<(), String> { #[cfg(target_os = "windows")] { use std::os::windows::process::CommandExt; let mut cmd = StdCommand::new("cmd"); cmd.args(["/c", &exe_path]); if let Some(a) = &args { let trimmed = a.trim(); if !trimmed.is_empty() { cmd.raw_arg(trimmed); } } cmd.spawn().map_err(|e| format!("启动失败: {e}"))?; } #[cfg(not(target_os = "windows"))] { let mut cmd = StdCommand::new(&exe_path); if let Some(a) = &args { let trimmed = a.trim(); if !trimmed.is_empty() { cmd.args(trimmed.split_whitespace()); } } cmd.spawn().map_err(|e| format!("启动失败: {e}"))?; } Ok(()) }