use crate::error::format_skill_error; use crate::services::skill::SkillState; use crate::services::{Skill, SkillRepo, SkillService}; use crate::store::AppState; use chrono::Utc; use std::sync::Arc; use tauri::State; pub struct SkillServiceState(pub Arc); #[tauri::command] pub async fn get_skills( service: State<'_, SkillServiceState>, app_state: State<'_, AppState>, ) -> Result, String> { let repos = { let config = app_state.config.read().map_err(|e| e.to_string())?; config.skills.repos.clone() }; service .0 .list_skills(repos) .await .map_err(|e| e.to_string()) } #[tauri::command] pub async fn install_skill( directory: String, service: State<'_, SkillServiceState>, app_state: State<'_, AppState>, ) -> Result { // 先在不持有写锁的情况下收集仓库与技能信息 let repos = { let config = app_state.config.read().map_err(|e| e.to_string())?; config.skills.repos.clone() }; let skills = service .0 .list_skills(repos) .await .map_err(|e| e.to_string())?; let skill = skills .iter() .find(|s| s.directory.eq_ignore_ascii_case(&directory)) .ok_or_else(|| { format_skill_error( "SKILL_NOT_FOUND", &[("directory", &directory)], Some("checkRepoUrl"), ) })?; if !skill.installed { let repo = SkillRepo { owner: skill.repo_owner.clone().ok_or_else(|| { format_skill_error( "MISSING_REPO_INFO", &[("directory", &directory), ("field", "owner")], None, ) })?, name: skill.repo_name.clone().ok_or_else(|| { format_skill_error( "MISSING_REPO_INFO", &[("directory", &directory), ("field", "name")], None, ) })?, branch: skill .repo_branch .clone() .unwrap_or_else(|| "main".to_string()), enabled: true, skills_path: skill.skills_path.clone(), // 使用技能记录的 skills_path }; service .0 .install_skill(directory.clone(), repo) .await .map_err(|e| e.to_string())?; } { let mut config = app_state.config.write().map_err(|e| e.to_string())?; config.skills.skills.insert( directory.clone(), SkillState { installed: true, installed_at: Utc::now(), }, ); } app_state.save().map_err(|e| e.to_string())?; Ok(true) } #[tauri::command] pub fn uninstall_skill( directory: String, service: State<'_, SkillServiceState>, app_state: State<'_, AppState>, ) -> Result { service .0 .uninstall_skill(directory.clone()) .map_err(|e| e.to_string())?; { let mut config = app_state.config.write().map_err(|e| e.to_string())?; config.skills.skills.remove(&directory); } app_state.save().map_err(|e| e.to_string())?; Ok(true) } #[tauri::command] pub fn get_skill_repos( _service: State<'_, SkillServiceState>, app_state: State<'_, AppState>, ) -> Result, String> { let config = app_state.config.read().map_err(|e| e.to_string())?; Ok(config.skills.repos.clone()) } #[tauri::command] pub fn add_skill_repo( repo: SkillRepo, service: State<'_, SkillServiceState>, app_state: State<'_, AppState>, ) -> Result { { let mut config = app_state.config.write().map_err(|e| e.to_string())?; service .0 .add_repo(&mut config.skills, repo) .map_err(|e| e.to_string())?; } app_state.save().map_err(|e| e.to_string())?; Ok(true) } #[tauri::command] pub fn remove_skill_repo( owner: String, name: String, service: State<'_, SkillServiceState>, app_state: State<'_, AppState>, ) -> Result { { let mut config = app_state.config.write().map_err(|e| e.to_string())?; service .0 .remove_repo(&mut config.skills, owner, name) .map_err(|e| e.to_string())?; } app_state.save().map_err(|e| e.to_string())?; Ok(true) }