forked from devlive-community/codeforge
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathenv_manager.rs
More file actions
277 lines (239 loc) · 8.15 KB
/
env_manager.rs
File metadata and controls
277 lines (239 loc) · 8.15 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
use async_trait::async_trait;
use log::{error, info};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::path::PathBuf;
use tauri::{AppHandle, Emitter};
// 环境版本信息
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct EnvironmentVersion {
pub version: String,
pub download_url: String,
pub fallback_url: Option<String>, // 备用下载地址(如 GitHub URL)
pub install_path: Option<String>,
pub is_installed: bool,
pub size: Option<u64>,
pub release_date: Option<String>,
}
// 环境管理器响应
#[derive(Debug, Serialize, Deserialize)]
pub struct EnvironmentInfo {
pub language: String,
pub current_version: Option<String>,
pub installed_versions: Vec<EnvironmentVersion>,
pub available_versions: Vec<EnvironmentVersion>,
}
// 下载进度事件
#[derive(Debug, Serialize, Clone)]
pub struct DownloadProgress {
pub language: String,
pub version: String,
pub downloaded: u64,
pub total: u64,
pub percentage: f64,
pub status: DownloadStatus,
}
#[derive(Debug, Serialize, Clone)]
#[serde(rename_all = "lowercase")]
pub enum DownloadStatus {
Downloading,
Extracting,
Installing,
Completed,
_Failed,
}
// 语言环境提供者特征
#[async_trait]
pub trait EnvironmentProvider: Send + Sync {
// 获取语言名称
fn get_language(&self) -> &'static str;
// 获取可用版本列表
async fn fetch_available_versions(&self) -> Result<Vec<EnvironmentVersion>, String>;
// 获取已安装版本列表
async fn get_installed_versions(&self) -> Result<Vec<EnvironmentVersion>, String>;
// 下载并安装指定版本
async fn download_and_install(
&self,
version: &str,
app_handle: AppHandle,
) -> Result<String, String>;
// 切换到指定版本
async fn switch_version(&self, version: &str) -> Result<(), String>;
// 获取当前激活的版本
async fn get_current_version(&self) -> Result<Option<String>, String>;
// 获取安装目录
#[allow(dead_code)]
fn get_install_dir(&self) -> PathBuf;
}
// 环境管理器
pub struct EnvironmentManager {
providers: HashMap<String, Box<dyn EnvironmentProvider>>,
}
impl EnvironmentManager {
pub fn new() -> Self {
Self {
providers: HashMap::new(),
}
}
pub fn register_provider(&mut self, provider: Box<dyn EnvironmentProvider>) {
let language = provider.get_language().to_string();
info!("注册环境提供者: {}", language);
self.providers.insert(language, provider);
}
pub async fn get_environment_info(&self, language: &str) -> Result<EnvironmentInfo, String> {
let provider = self
.providers
.get(language)
.ok_or_else(|| format!("暂未支持 {} 语言,请前往 github 提供 issues", language))?;
info!("获取 {} 环境信息", language);
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();
Ok(EnvironmentInfo {
language: language.to_string(),
current_version,
installed_versions,
available_versions,
})
}
pub async fn download_and_install_version(
&self,
language: &str,
version: &str,
app_handle: AppHandle,
) -> Result<String, String> {
let provider = self
.providers
.get(language)
.ok_or_else(|| format!("暂未支持 {} 语言,请前往 github 提供 issues", language))?;
info!("开始下载并安装 {} 版本 {}", language, version);
provider.download_and_install(version, app_handle).await
}
pub async fn switch_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.switch_version(version).await
}
pub fn get_supported_languages(&self) -> Vec<String> {
self.providers.keys().cloned().collect()
}
}
// 辅助函数:发送下载进度事件
pub fn emit_download_progress(
app_handle: &AppHandle,
language: &str,
version: &str,
downloaded: u64,
total: u64,
status: DownloadStatus,
) {
let percentage = if total > 0 {
(downloaded as f64 / total as f64) * 100.0
} else {
0.0
};
let progress = DownloadProgress {
language: language.to_string(),
version: version.to_string(),
downloaded,
total,
percentage,
status,
};
if let Err(e) = app_handle.emit("env-download-progress", progress) {
error!("发送下载进度事件失败: {}", e);
}
}
// 将 GitHub 下载 URL 转换为 CDN 镜像 URL
pub fn convert_to_cdn_url(
original_url: &str,
language: &str,
version: &str,
) -> Result<String, String> {
use crate::config::get_app_config_internal;
let config = get_app_config_internal().map_err(|e| format!("获取配置失败: {}", e))?;
let mirror_config = config.environment_mirror.as_ref();
let enabled = mirror_config.and_then(|m| m.enabled).unwrap_or(false);
if !enabled {
return Ok(original_url.to_string());
}
let base_url = mirror_config
.and_then(|m| m.base_url.as_ref())
.ok_or_else(|| "CDN 基础 URL 未配置".to_string())?;
// 从原始 URL 中提取文件名
let file_name = original_url
.split('/')
.last()
.ok_or_else(|| "无效的下载 URL".to_string())?;
// 构建 CDN URL: {base_url}/{language}/{version}/{filename}
let cdn_url = format!(
"{}/{}/{}/{}",
base_url.trim_end_matches('/'),
language,
version,
file_name
);
info!("转换下载 URL: {} -> {}", original_url, cdn_url);
Ok(cdn_url)
}
// 尝试从 CDN 下载,失败则回退到原始 URL
pub async fn download_with_fallback(
client: &reqwest::Client,
original_url: &str,
language: &str,
version: &str,
) -> Result<reqwest::Response, String> {
use crate::config::get_app_config_internal;
// 检查是否启用自动回退
let fallback_enabled = get_app_config_internal()
.ok()
.and_then(|config| config.environment_mirror)
.and_then(|mirror| mirror.fallback_enabled)
.unwrap_or(false);
// 首先尝试从 CDN 下载
match convert_to_cdn_url(original_url, language, version) {
Ok(cdn_url) if cdn_url != original_url => {
info!("尝试从 CDN 下载: {}", cdn_url);
match client.get(&cdn_url).send().await {
Ok(response) if response.status().is_success() => {
info!("CDN 下载成功");
return Ok(response);
}
Ok(response) => {
let status = response.status();
if fallback_enabled {
info!("CDN 下载失败 (HTTP {}), 回退到原始 URL", status);
} else {
return Err(format!("CDN 下载失败 (HTTP {}), 未启用自动回退", status));
}
}
Err(e) => {
if fallback_enabled {
info!("CDN 下载失败 ({}), 回退到原始 URL", e);
} else {
return Err(format!("CDN 下载失败 ({}), 未启用自动回退", e));
}
}
}
}
Ok(_) => {
info!("CDN 镜像未启用,使用原始 URL");
}
Err(e) => {
info!("CDN URL 转换失败 ({}), 使用原始 URL", e);
}
}
// 回退到原始 URL
info!("从原始 URL 下载: {}", original_url);
client
.get(original_url)
.send()
.await
.map_err(|e| format!("下载失败: {}", e))
}