Skip to content

Commit 9bc965c

Browse files
authored
Merge pull request #65 from qianmoQ/dev-25.0.5
feat: 支持卸载已安装版本
2 parents 507fc2d + 91ec62b commit 9bc965c

File tree

13 files changed

+510
-199
lines changed

13 files changed

+510
-199
lines changed

src-tauri/src/config.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -319,11 +319,24 @@ pub fn get_app_config_internal() -> Result<AppConfig, String> {
319319
}
320320

321321
#[command]
322-
pub async fn update_app_config(config: AppConfig) -> Result<(), String> {
322+
pub async fn update_app_config(
323+
config: AppConfig,
324+
app_handle: tauri::AppHandle,
325+
) -> Result<(), String> {
326+
use tauri::Emitter;
327+
323328
let mut guard = get_config_manager()?;
324329
if let Some(config_manager) = guard.as_mut() {
325330
config_manager.config = config;
326-
config_manager.save_config()
331+
let result = config_manager.save_config();
332+
333+
// 保存成功后发送配置更新事件
334+
if result.is_ok() {
335+
app_handle.emit("config-updated", ()).ok();
336+
info!("配置已更新,已发送 config-updated 事件");
337+
}
338+
339+
result
327340
} else {
328341
Err("配置管理器未初始化".to_string())
329342
}

src-tauri/src/env_commands.rs

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,15 +46,9 @@ pub async fn switch_environment_version(
4646
) -> Result<(), String> {
4747
info!("切换 {} 到版本 {}", language, version);
4848
let manager = env_manager.lock().await;
49-
let result = manager.switch_version(&language, &version).await;
50-
51-
if result.is_ok() {
52-
// 发送配置更新事件通知前端刷新配置
53-
app_handle.emit("config-updated", ()).ok();
54-
info!("已发送配置更新事件");
55-
}
56-
57-
result
49+
manager
50+
.switch_version(&language, &version, app_handle)
51+
.await
5852
}
5953

6054
#[tauri::command]
@@ -64,3 +58,14 @@ pub async fn get_supported_environment_languages(
6458
let manager = env_manager.lock().await;
6559
Ok(manager.get_supported_languages())
6660
}
61+
62+
#[tauri::command]
63+
pub async fn uninstall_environment_version(
64+
language: String,
65+
version: String,
66+
env_manager: State<'_, EnvironmentManagerState>,
67+
) -> Result<(), String> {
68+
info!("卸载 {} 版本 {}", language, version);
69+
let manager = env_manager.lock().await;
70+
manager.uninstall_version(&language, &version).await
71+
}

src-tauri/src/env_manager.rs

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use async_trait::async_trait;
2-
use log::{error, info};
2+
use log::{error, info, warn};
33
use serde::{Deserialize, Serialize};
44
use std::collections::HashMap;
55
use std::path::PathBuf;
@@ -24,6 +24,7 @@ pub struct EnvironmentInfo {
2424
pub current_version: Option<String>,
2525
pub installed_versions: Vec<EnvironmentVersion>,
2626
pub available_versions: Vec<EnvironmentVersion>,
27+
pub error: Option<String>, // 错误信息(如获取可用版本失败)
2728
}
2829

2930
// 下载进度事件
@@ -67,14 +68,17 @@ pub trait EnvironmentProvider: Send + Sync {
6768
) -> Result<String, String>;
6869

6970
// 切换到指定版本
70-
async fn switch_version(&self, version: &str) -> Result<(), String>;
71+
async fn switch_version(&self, version: &str, app_handle: AppHandle) -> Result<(), String>;
7172

7273
// 获取当前激活的版本
7374
async fn get_current_version(&self) -> Result<Option<String>, String>;
7475

7576
// 获取安装目录
7677
#[allow(dead_code)]
7778
fn get_install_dir(&self) -> PathBuf;
79+
80+
// 卸载指定版本
81+
async fn uninstall_version(&self, version: &str) -> Result<(), String>;
7882
}
7983

8084
// 环境管理器
@@ -105,16 +109,22 @@ impl EnvironmentManager {
105109

106110
let current_version = provider.get_current_version().await.ok().flatten();
107111
let installed_versions = provider.get_installed_versions().await.unwrap_or_default();
108-
let available_versions = provider
109-
.fetch_available_versions()
110-
.await
111-
.unwrap_or_default();
112+
113+
// 获取可用版本,如果失败也要返回已安装版本,并在错误信息中说明
114+
let (available_versions, error) = match provider.fetch_available_versions().await {
115+
Ok(versions) => (versions, None),
116+
Err(e) => {
117+
warn!("获取可用版本失败: {}", e);
118+
(vec![], Some(format!("获取可用版本失败: {}", e)))
119+
}
120+
};
112121

113122
Ok(EnvironmentInfo {
114123
language: language.to_string(),
115124
current_version,
116125
installed_versions,
117126
available_versions,
127+
error,
118128
})
119129
}
120130

@@ -133,19 +143,34 @@ impl EnvironmentManager {
133143
provider.download_and_install(version, app_handle).await
134144
}
135145

136-
pub async fn switch_version(&self, language: &str, version: &str) -> Result<(), String> {
146+
pub async fn switch_version(
147+
&self,
148+
language: &str,
149+
version: &str,
150+
app_handle: AppHandle,
151+
) -> Result<(), String> {
137152
let provider = self
138153
.providers
139154
.get(language)
140155
.ok_or_else(|| format!("暂未支持 {} 语言,请前往 github 提供 issues", language))?;
141156

142157
info!("切换 {} 到版本 {}", language, version);
143-
provider.switch_version(version).await
158+
provider.switch_version(version, app_handle).await
144159
}
145160

146161
pub fn get_supported_languages(&self) -> Vec<String> {
147162
self.providers.keys().cloned().collect()
148163
}
164+
165+
pub async fn uninstall_version(&self, language: &str, version: &str) -> Result<(), String> {
166+
let provider = self
167+
.providers
168+
.get(language)
169+
.ok_or_else(|| format!("暂未支持 {} 语言,请前往 github 提供 issues", language))?;
170+
171+
info!("卸载 {} 版本 {}", language, version);
172+
provider.uninstall_version(version).await
173+
}
149174
}
150175

151176
// 辅助函数:发送下载进度事件

src-tauri/src/env_providers/clojure.rs

Lines changed: 45 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use super::metadata::{Metadata, fetch_metadata_from_cdn, is_cdn_enabled, is_fallback_enabled};
12
use crate::env_manager::{
23
DownloadStatus, EnvironmentProvider, EnvironmentVersion, emit_download_progress,
34
};
@@ -22,25 +23,6 @@ struct GithubAsset {
2223
size: u64,
2324
}
2425

25-
// CDN Metadata 结构
26-
#[derive(Debug, Deserialize, Serialize, Clone)]
27-
struct MetadataRelease {
28-
version: String, // 版本号,如 "1.11.1.1262"
29-
display_name: String, // 显示名称,如 "Clojure 1.11.1.1262"
30-
published_at: String, // 发布时间
31-
download_url: String, // CDN 下载地址
32-
github_url: String, // GitHub 官方下载地址(作为备用)
33-
file_name: String, // 文件名,如 "clojure-tools-1.11.1.1262.tar.gz"
34-
size: u64, // 文件大小(字节)
35-
supported_platforms: Vec<String>, // 支持的平台,如 ["macos", "linux", "windows"]
36-
}
37-
38-
#[derive(Debug, Deserialize, Serialize)]
39-
struct Metadata {
40-
language: String, // 语言名称 "clojure"
41-
releases: Vec<MetadataRelease>,
42-
}
43-
4426
#[derive(Debug, Deserialize, Serialize)]
4527
struct CachedReleases {
4628
releases: Vec<GithubRelease>,
@@ -185,56 +167,6 @@ impl ClojureEnvironmentProvider {
185167
Ok(versions)
186168
}
187169

188-
// 从 CDN 获取 metadata.json
189-
async fn fetch_metadata_from_cdn(&self) -> Result<Metadata, String> {
190-
use crate::config::get_app_config_internal;
191-
192-
let config = get_app_config_internal().map_err(|e| format!("读取配置失败: {}", e))?;
193-
194-
let cdn_enabled = config
195-
.environment_mirror
196-
.as_ref()
197-
.and_then(|m| m.enabled)
198-
.unwrap_or(false);
199-
200-
if !cdn_enabled {
201-
return Err("CDN 未启用".to_string());
202-
}
203-
204-
let base_url = config
205-
.environment_mirror
206-
.as_ref()
207-
.and_then(|m| m.base_url.as_ref())
208-
.ok_or("CDN 地址未配置")?;
209-
210-
let metadata_url = format!("{}/clojure/metadata.json", base_url);
211-
info!("从 CDN 获取 Clojure metadata: {}", metadata_url);
212-
213-
let client = reqwest::Client::builder()
214-
.user_agent("CodeForge")
215-
.timeout(Duration::from_secs(30))
216-
.build()
217-
.map_err(|e| format!("创建 HTTP 客户端失败: {}", e))?;
218-
219-
let response = client
220-
.get(&metadata_url)
221-
.send()
222-
.await
223-
.map_err(|e| format!("请求 CDN metadata 失败: {}", e))?;
224-
225-
if !response.status().is_success() {
226-
return Err(format!("CDN 返回错误状态码: {}", response.status()));
227-
}
228-
229-
let metadata: Metadata = response
230-
.json()
231-
.await
232-
.map_err(|e| format!("解析 metadata.json 失败: {}", e))?;
233-
234-
info!("成功从 CDN 获取 {} 个版本", metadata.releases.len());
235-
Ok(metadata)
236-
}
237-
238170
async fn fetch_github_releases(&self) -> Result<Vec<GithubRelease>, String> {
239171
if let Some(cached_releases) = self.read_cache() {
240172
return Ok(cached_releases);
@@ -594,7 +526,12 @@ impl ClojureEnvironmentProvider {
594526
Ok(())
595527
}
596528

597-
async fn update_plugin_config(&self, version: &str, install_path: &str) -> Result<(), String> {
529+
async fn update_plugin_config(
530+
&self,
531+
version: &str,
532+
install_path: &str,
533+
app_handle: &AppHandle,
534+
) -> Result<(), String> {
598535
use crate::config::{get_app_config_internal, update_app_config};
599536

600537
info!(
@@ -616,7 +553,7 @@ impl ClojureEnvironmentProvider {
616553
}
617554
}
618555

619-
update_app_config(config)
556+
update_app_config(config, app_handle.clone())
620557
.await
621558
.map_err(|e| format!("保存配置失败: {}", e))?;
622559

@@ -631,30 +568,26 @@ impl EnvironmentProvider for ClojureEnvironmentProvider {
631568
}
632569

633570
async fn fetch_available_versions(&self) -> Result<Vec<EnvironmentVersion>, String> {
634-
use crate::config::get_app_config_internal;
635-
636-
// 优先尝试从 CDN 获取 metadata.json
637-
match self.fetch_metadata_from_cdn().await {
638-
Ok(metadata) => {
639-
info!("使用 CDN metadata 获取版本列表");
640-
return self.parse_metadata_to_versions(metadata);
641-
}
642-
Err(e) => {
643-
warn!("CDN metadata 获取失败: {}", e);
571+
// 检查 CDN 是否启用
572+
if is_cdn_enabled() {
573+
match fetch_metadata_from_cdn("clojure").await {
574+
Ok(metadata) => {
575+
info!("使用 CDN metadata 获取版本列表");
576+
return self.parse_metadata_to_versions(metadata);
577+
}
578+
Err(e) => {
579+
warn!("CDN metadata 获取失败: {}", e);
644580

645-
// 检查是否启用 fallback
646-
let fallback_enabled = get_app_config_internal()
647-
.ok()
648-
.and_then(|config| config.environment_mirror)
649-
.and_then(|mirror| mirror.fallback_enabled)
650-
.unwrap_or(false);
581+
// 检查是否启用 fallback
582+
if !is_fallback_enabled() {
583+
return Err(format!("CDN metadata 获取失败,未启用自动回退: {}", e));
584+
}
651585

652-
if !fallback_enabled {
653-
return Err(format!("CDN metadata 获取失败,未启用自动回退: {}", e));
586+
info!("fallback 已启用,回退到 GitHub API");
654587
}
655-
656-
info!("fallback 已启用,回退到 GitHub API");
657588
}
589+
} else {
590+
info!("CDN 未启用,使用 GitHub API");
658591
}
659592

660593
let releases = self.fetch_github_releases().await?;
@@ -794,7 +727,7 @@ impl EnvironmentProvider for ClojureEnvironmentProvider {
794727
// 清理临时解压目录
795728
std::fs::remove_dir_all(&temp_extract_dir).ok();
796729

797-
self.update_plugin_config(version, &install_path.to_string_lossy())
730+
self.update_plugin_config(version, &install_path.to_string_lossy(), &app_handle)
798731
.await?;
799732

800733
emit_download_progress(
@@ -810,7 +743,7 @@ impl EnvironmentProvider for ClojureEnvironmentProvider {
810743
Ok(install_path.to_string_lossy().to_string())
811744
}
812745

813-
async fn switch_version(&self, version: &str) -> Result<(), String> {
746+
async fn switch_version(&self, version: &str, app_handle: AppHandle) -> Result<(), String> {
814747
info!("切换 Clojure 版本到 {}", version);
815748

816749
if !self.is_version_installed(version) {
@@ -819,7 +752,7 @@ impl EnvironmentProvider for ClojureEnvironmentProvider {
819752

820753
let install_path = self.get_version_install_path(version);
821754

822-
self.update_plugin_config(version, &install_path.to_string_lossy())
755+
self.update_plugin_config(version, &install_path.to_string_lossy(), &app_handle)
823756
.await?;
824757

825758
info!("成功切换到 Clojure {}", version);
@@ -854,4 +787,22 @@ impl EnvironmentProvider for ClojureEnvironmentProvider {
854787
fn get_install_dir(&self) -> PathBuf {
855788
self.install_dir.clone()
856789
}
790+
791+
async fn uninstall_version(&self, version: &str) -> Result<(), String> {
792+
let version_dir = self.install_dir.join(version);
793+
794+
if !version_dir.exists() {
795+
return Err(format!("版本 {} 未安装", version));
796+
}
797+
798+
let current_version = self.get_current_version().await.ok().flatten();
799+
if current_version.as_deref() == Some(version) {
800+
return Err(format!("无法卸载当前正在使用的版本 {}", version));
801+
}
802+
803+
std::fs::remove_dir_all(&version_dir).map_err(|e| format!("删除版本目录失败: {}", e))?;
804+
805+
info!("已卸载 Clojure 版本 {}", version);
806+
Ok(())
807+
}
857808
}

0 commit comments

Comments
 (0)