Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions src-tauri/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,11 +319,24 @@ pub fn get_app_config_internal() -> Result<AppConfig, String> {
}

#[command]
pub async fn update_app_config(config: AppConfig) -> Result<(), String> {
pub async fn update_app_config(
config: AppConfig,
app_handle: tauri::AppHandle,
) -> Result<(), String> {
use tauri::Emitter;

let mut guard = get_config_manager()?;
if let Some(config_manager) = guard.as_mut() {
config_manager.config = config;
config_manager.save_config()
let result = config_manager.save_config();

// 保存成功后发送配置更新事件
if result.is_ok() {
app_handle.emit("config-updated", ()).ok();
info!("配置已更新,已发送 config-updated 事件");
}

result
} else {
Err("配置管理器未初始化".to_string())
}
Expand Down
23 changes: 14 additions & 9 deletions src-tauri/src/env_commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,9 @@ pub async fn switch_environment_version(
) -> Result<(), String> {
info!("切换 {} 到版本 {}", language, version);
let manager = env_manager.lock().await;
let result = manager.switch_version(&language, &version).await;

if result.is_ok() {
// 发送配置更新事件通知前端刷新配置
app_handle.emit("config-updated", ()).ok();
info!("已发送配置更新事件");
}

result
manager
.switch_version(&language, &version, app_handle)
.await
}

#[tauri::command]
Expand All @@ -64,3 +58,14 @@ pub async fn get_supported_environment_languages(
let manager = env_manager.lock().await;
Ok(manager.get_supported_languages())
}

#[tauri::command]
pub async fn uninstall_environment_version(
language: String,
version: String,
env_manager: State<'_, EnvironmentManagerState>,
) -> Result<(), String> {
info!("卸载 {} 版本 {}", language, version);
let manager = env_manager.lock().await;
manager.uninstall_version(&language, &version).await
}
41 changes: 33 additions & 8 deletions src-tauri/src/env_manager.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use async_trait::async_trait;
use log::{error, info};
use log::{error, info, warn};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
Expand All @@ -24,6 +24,7 @@ pub struct EnvironmentInfo {
pub current_version: Option<String>,
pub installed_versions: Vec<EnvironmentVersion>,
pub available_versions: Vec<EnvironmentVersion>,
pub error: Option<String>, // 错误信息(如获取可用版本失败)
}

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

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

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

// 获取安装目录
#[allow(dead_code)]
fn get_install_dir(&self) -> PathBuf;

// 卸载指定版本
async fn uninstall_version(&self, version: &str) -> Result<(), String>;
}

// 环境管理器
Expand Down Expand Up @@ -105,16 +109,22 @@ impl EnvironmentManager {

let current_version = provider.get_current_version().await.ok().flatten();
let installed_versions = provider.get_installed_versions().await.unwrap_or_default();
let available_versions = provider
.fetch_available_versions()
.await
.unwrap_or_default();

// 获取可用版本,如果失败也要返回已安装版本,并在错误信息中说明
let (available_versions, error) = match provider.fetch_available_versions().await {
Ok(versions) => (versions, None),
Err(e) => {
warn!("获取可用版本失败: {}", e);
(vec![], Some(format!("获取可用版本失败: {}", e)))
}
};

Ok(EnvironmentInfo {
language: language.to_string(),
current_version,
installed_versions,
available_versions,
error,
})
}

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

pub async fn switch_version(&self, language: &str, version: &str) -> Result<(), String> {
pub async fn switch_version(
&self,
language: &str,
version: &str,
app_handle: AppHandle,
) -> Result<(), String> {
let provider = self
.providers
.get(language)
.ok_or_else(|| format!("暂未支持 {} 语言,请前往 github 提供 issues", language))?;

info!("切换 {} 到版本 {}", language, version);
provider.switch_version(version).await
provider.switch_version(version, app_handle).await
}

pub fn get_supported_languages(&self) -> Vec<String> {
self.providers.keys().cloned().collect()
}

pub async fn uninstall_version(&self, language: &str, version: &str) -> Result<(), String> {
let provider = self
.providers
.get(language)
.ok_or_else(|| format!("暂未支持 {} 语言,请前往 github 提供 issues", language))?;

info!("卸载 {} 版本 {}", language, version);
provider.uninstall_version(version).await
}
}

// 辅助函数:发送下载进度事件
Expand Down
139 changes: 45 additions & 94 deletions src-tauri/src/env_providers/clojure.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::metadata::{Metadata, fetch_metadata_from_cdn, is_cdn_enabled, is_fallback_enabled};
use crate::env_manager::{
DownloadStatus, EnvironmentProvider, EnvironmentVersion, emit_download_progress,
};
Expand All @@ -22,25 +23,6 @@ struct GithubAsset {
size: u64,
}

// CDN Metadata 结构
#[derive(Debug, Deserialize, Serialize, Clone)]
struct MetadataRelease {
version: String, // 版本号,如 "1.11.1.1262"
display_name: String, // 显示名称,如 "Clojure 1.11.1.1262"
published_at: String, // 发布时间
download_url: String, // CDN 下载地址
github_url: String, // GitHub 官方下载地址(作为备用)
file_name: String, // 文件名,如 "clojure-tools-1.11.1.1262.tar.gz"
size: u64, // 文件大小(字节)
supported_platforms: Vec<String>, // 支持的平台,如 ["macos", "linux", "windows"]
}

#[derive(Debug, Deserialize, Serialize)]
struct Metadata {
language: String, // 语言名称 "clojure"
releases: Vec<MetadataRelease>,
}

#[derive(Debug, Deserialize, Serialize)]
struct CachedReleases {
releases: Vec<GithubRelease>,
Expand Down Expand Up @@ -185,56 +167,6 @@ impl ClojureEnvironmentProvider {
Ok(versions)
}

// 从 CDN 获取 metadata.json
async fn fetch_metadata_from_cdn(&self) -> Result<Metadata, String> {
use crate::config::get_app_config_internal;

let config = get_app_config_internal().map_err(|e| format!("读取配置失败: {}", e))?;

let cdn_enabled = config
.environment_mirror
.as_ref()
.and_then(|m| m.enabled)
.unwrap_or(false);

if !cdn_enabled {
return Err("CDN 未启用".to_string());
}

let base_url = config
.environment_mirror
.as_ref()
.and_then(|m| m.base_url.as_ref())
.ok_or("CDN 地址未配置")?;

let metadata_url = format!("{}/clojure/metadata.json", base_url);
info!("从 CDN 获取 Clojure metadata: {}", metadata_url);

let client = reqwest::Client::builder()
.user_agent("CodeForge")
.timeout(Duration::from_secs(30))
.build()
.map_err(|e| format!("创建 HTTP 客户端失败: {}", e))?;

let response = client
.get(&metadata_url)
.send()
.await
.map_err(|e| format!("请求 CDN metadata 失败: {}", e))?;

if !response.status().is_success() {
return Err(format!("CDN 返回错误状态码: {}", response.status()));
}

let metadata: Metadata = response
.json()
.await
.map_err(|e| format!("解析 metadata.json 失败: {}", e))?;

info!("成功从 CDN 获取 {} 个版本", metadata.releases.len());
Ok(metadata)
}

async fn fetch_github_releases(&self) -> Result<Vec<GithubRelease>, String> {
if let Some(cached_releases) = self.read_cache() {
return Ok(cached_releases);
Expand Down Expand Up @@ -594,7 +526,12 @@ impl ClojureEnvironmentProvider {
Ok(())
}

async fn update_plugin_config(&self, version: &str, install_path: &str) -> Result<(), String> {
async fn update_plugin_config(
&self,
version: &str,
install_path: &str,
app_handle: &AppHandle,
) -> Result<(), String> {
use crate::config::{get_app_config_internal, update_app_config};

info!(
Expand All @@ -616,7 +553,7 @@ impl ClojureEnvironmentProvider {
}
}

update_app_config(config)
update_app_config(config, app_handle.clone())
.await
.map_err(|e| format!("保存配置失败: {}", e))?;

Expand All @@ -631,30 +568,26 @@ impl EnvironmentProvider for ClojureEnvironmentProvider {
}

async fn fetch_available_versions(&self) -> Result<Vec<EnvironmentVersion>, String> {
use crate::config::get_app_config_internal;

// 优先尝试从 CDN 获取 metadata.json
match self.fetch_metadata_from_cdn().await {
Ok(metadata) => {
info!("使用 CDN metadata 获取版本列表");
return self.parse_metadata_to_versions(metadata);
}
Err(e) => {
warn!("CDN metadata 获取失败: {}", e);
// 检查 CDN 是否启用
if is_cdn_enabled() {
match fetch_metadata_from_cdn("clojure").await {
Ok(metadata) => {
info!("使用 CDN metadata 获取版本列表");
return self.parse_metadata_to_versions(metadata);
}
Err(e) => {
warn!("CDN metadata 获取失败: {}", e);

// 检查是否启用 fallback
let fallback_enabled = get_app_config_internal()
.ok()
.and_then(|config| config.environment_mirror)
.and_then(|mirror| mirror.fallback_enabled)
.unwrap_or(false);
// 检查是否启用 fallback
if !is_fallback_enabled() {
return Err(format!("CDN metadata 获取失败,未启用自动回退: {}", e));
}

if !fallback_enabled {
return Err(format!("CDN metadata 获取失败,未启用自动回退: {}", e));
info!("fallback 已启用,回退到 GitHub API");
}

info!("fallback 已启用,回退到 GitHub API");
}
} else {
info!("CDN 未启用,使用 GitHub API");
}

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

self.update_plugin_config(version, &install_path.to_string_lossy())
self.update_plugin_config(version, &install_path.to_string_lossy(), &app_handle)
.await?;

emit_download_progress(
Expand All @@ -810,7 +743,7 @@ impl EnvironmentProvider for ClojureEnvironmentProvider {
Ok(install_path.to_string_lossy().to_string())
}

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

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

let install_path = self.get_version_install_path(version);

self.update_plugin_config(version, &install_path.to_string_lossy())
self.update_plugin_config(version, &install_path.to_string_lossy(), &app_handle)
.await?;

info!("成功切换到 Clojure {}", version);
Expand Down Expand Up @@ -854,4 +787,22 @@ impl EnvironmentProvider for ClojureEnvironmentProvider {
fn get_install_dir(&self) -> PathBuf {
self.install_dir.clone()
}

async fn uninstall_version(&self, version: &str) -> Result<(), String> {
let version_dir = self.install_dir.join(version);

if !version_dir.exists() {
return Err(format!("版本 {} 未安装", version));
}

let current_version = self.get_current_version().await.ok().flatten();
if current_version.as_deref() == Some(version) {
return Err(format!("无法卸载当前正在使用的版本 {}", version));
}

std::fs::remove_dir_all(&version_dir).map_err(|e| format!("删除版本目录失败: {}", e))?;

info!("已卸载 Clojure 版本 {}", version);
Ok(())
}
}
Loading
Loading