Skip to content

Commit ccbe290

Browse files
lius-newcursoragent
andcommitted
style(rust): planner.rs 代码格式调整
Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 225f53b commit ccbe290

1 file changed

Lines changed: 135 additions & 138 deletions

File tree

src-tauri/src/updater/planner.rs

Lines changed: 135 additions & 138 deletions
Original file line numberDiff line numberDiff line change
@@ -1,138 +1,135 @@
1-
use crate::config::get_config;
2-
/// 更新计划生成模块
3-
///
4-
/// 负责对比本地文件 SHA256 与服务器版本,生成更新任务
5-
use crate::updater::types::{Artifact, CheckResponse, UpdateTask};
6-
use anyhow::{Context, Result};
7-
use sha2::{Digest, Sha256};
8-
use std::fs::File;
9-
use std::io::Read;
10-
use std::path::{Path, PathBuf};
11-
12-
/// 计算文件的完整 SHA256 哈希
13-
pub fn calculate_file_hash(file_path: &Path) -> Result<String> {
14-
let mut file =
15-
File::open(file_path).with_context(|| format!("无法打开文件: {}", file_path.display()))?;
16-
17-
let mut hasher = Sha256::new();
18-
let mut buffer = vec![0; 8192]; // 8KB 缓冲区
19-
20-
loop {
21-
let bytes_read = file
22-
.read(&mut buffer)
23-
.with_context(|| format!("读取文件失败: {}", file_path.display()))?;
24-
25-
if bytes_read == 0 {
26-
break;
27-
}
28-
29-
hasher.update(&buffer[..bytes_read]);
30-
}
31-
32-
Ok(format!("{:x}", hasher.finalize()))
33-
}
34-
35-
/// 获取资源文件的默认路径(当前 exe 同目录)
36-
fn get_resource_path(resource_name: &str) -> Result<PathBuf> {
37-
let current_exe = std::env::current_exe().context("无法获取当前可执行文件路径")?;
38-
let exe_dir = current_exe.parent().context("无法获取可执行文件目录")?;
39-
Ok(exe_dir.join(resource_name))
40-
}
41-
42-
/// 获取下载临时目录
43-
/// 优先读取配置 `updater.updater_temp_dir`(相对路径基于 exe 同目录),
44-
/// 未配置则使用 exe 同目录下的 `updates`
45-
fn get_temp_dir() -> Result<PathBuf> {
46-
let current_exe = std::env::current_exe().context("无法获取当前可执行文件路径")?;
47-
let exe_dir = current_exe.parent().context("无法获取可执行文件目录")?;
48-
49-
let cfg = get_config();
50-
if let Some(ref custom) = cfg.updater.updater_temp_dir {
51-
let p = PathBuf::from(custom);
52-
let dir = if p.is_absolute() { p } else { exe_dir.join(p) };
53-
std::fs::create_dir_all(&dir).ok();
54-
return Ok(dir);
55-
}
56-
57-
let default_dir = exe_dir.join("updates");
58-
std::fs::create_dir_all(&default_dir).ok();
59-
Ok(default_dir)
60-
}
61-
62-
/// 根据 artifact 获取目标路径
63-
/// 优先使用 install_path,如果为空则使用当前目录 + resource_name
64-
fn get_target_path(artifact: &Artifact) -> Result<PathBuf> {
65-
if let Some(ref install_path) = artifact.install_path {
66-
let path = PathBuf::from(install_path);
67-
68-
// 规范化路径,防止路径攻击
69-
let normalized_path = path.canonicalize().or_else(|_| {
70-
if path.components().any(|c| matches!(c, std::path::Component::ParentDir)) {
71-
return Err(anyhow::anyhow!("路径包含非法组件: {}", install_path));
72-
}
73-
Ok(path)
74-
})?;
75-
76-
// 使用服务器提供的 install_path
77-
Ok(normalized_path)
78-
} else {
79-
// 使用当前 exe 同级目录 + resource_name
80-
get_resource_path(&artifact.resource_name)
81-
}
82-
}
83-
84-
/// 检查资源文件是否存在且哈希匹配
85-
fn check_resource_file(artifact: &Artifact) -> Result<bool> {
86-
let target_path = get_target_path(artifact)?;
87-
88-
// 文件不存在,需要更新
89-
if !target_path.exists() {
90-
return Ok(true);
91-
}
92-
93-
// 计算本地文件哈希
94-
let local_hash = calculate_file_hash(&target_path)?;
95-
let remote_hash = artifact.hash.to_lowercase();
96-
97-
// 哈希不匹配,需要更新
98-
Ok(local_hash.to_lowercase() != remote_hash)
99-
}
100-
101-
/// 生成更新计划
102-
///
103-
/// # 参数
104-
/// - `check_response`: 服务器检查响应
105-
///
106-
/// # 返回
107-
/// 返回需要更新的任务列表
108-
pub fn plan_updates(check_response: &CheckResponse) -> Result<Vec<UpdateTask>> {
109-
let mut tasks = Vec::new();
110-
111-
// 忽略 is_gray,仅基于本地实际 SHA256 与服务器版本比较
112-
// 统一处理所有版本类型:T9_CLIENT, DLL, OTHER_PROGRAM 等
113-
114-
// 遍历所有版本类型
115-
for (_version_type, artifacts) in &check_response.data.versions {
116-
for artifact in artifacts {
117-
// 检查是否需要更新
118-
let needs_update = check_resource_file(artifact)?;
119-
120-
if needs_update {
121-
// 获取目标路径(优先使用 install_path,为空则使用当前目录)
122-
let target_path = get_target_path(artifact)?;
123-
let exe_dir = target_path.parent().context("无法获取可执行文件目录")?;
124-
let temp_dir = get_temp_dir()?;
125-
let temp_path = temp_dir.join(format!("{}.tmp", artifact.resource_name));
126-
127-
tasks.push(UpdateTask {
128-
artifact: artifact.clone(),
129-
target_path: target_path.clone(),
130-
backup_path: Some(exe_dir.join(format!("{}.bak", artifact.resource_name))),
131-
temp_path,
132-
});
133-
}
134-
}
135-
}
136-
137-
Ok(tasks)
138-
}
1+
use crate::config::get_config;
2+
/// 更新计划生成模块
3+
///
4+
/// 负责对比本地文件 SHA256 与服务器版本,生成更新任务
5+
use crate::updater::types::{Artifact, CheckResponse, UpdateTask};
6+
use anyhow::{Context, Result};
7+
use sha2::{Digest, Sha256};
8+
use std::fs::File;
9+
use std::io::Read;
10+
use std::path::{Path, PathBuf};
11+
12+
/// 计算文件的完整 SHA256 哈希
13+
pub fn calculate_file_hash(file_path: &Path) -> Result<String> {
14+
let mut file =
15+
File::open(file_path).with_context(|| format!("无法打开文件: {}", file_path.display()))?;
16+
17+
let mut hasher = Sha256::new();
18+
let mut buffer = vec![0; 8192]; // 8KB 缓冲区
19+
20+
loop {
21+
let bytes_read = file
22+
.read(&mut buffer)
23+
.with_context(|| format!("读取文件失败: {}", file_path.display()))?;
24+
25+
if bytes_read == 0 {
26+
break;
27+
}
28+
29+
hasher.update(&buffer[..bytes_read]);
30+
}
31+
32+
Ok(format!("{:x}", hasher.finalize()))
33+
}
34+
35+
/// 获取资源文件的默认路径(当前 exe 同目录)
36+
fn get_resource_path(resource_name: &str) -> Result<PathBuf> {
37+
let current_exe = std::env::current_exe().context("无法获取当前可执行文件路径")?;
38+
let exe_dir = current_exe.parent().context("无法获取可执行文件目录")?;
39+
Ok(exe_dir.join(resource_name))
40+
}
41+
42+
/// 获取下载临时目录
43+
/// 优先读取配置 `updater.updater_temp_dir`(相对路径基于 exe 同目录),
44+
/// 未配置则使用 exe 同目录下的 `updates`
45+
fn get_temp_dir() -> Result<PathBuf> {
46+
let current_exe = std::env::current_exe().context("无法获取当前可执行文件路径")?;
47+
let exe_dir = current_exe.parent().context("无法获取可执行文件目录")?;
48+
49+
let cfg = get_config();
50+
if let Some(ref custom) = cfg.updater.updater_temp_dir {
51+
let p = PathBuf::from(custom);
52+
let dir = if p.is_absolute() { p } else { exe_dir.join(p) };
53+
std::fs::create_dir_all(&dir).ok();
54+
return Ok(dir);
55+
}
56+
57+
let default_dir = exe_dir.join("updates");
58+
std::fs::create_dir_all(&default_dir).ok();
59+
Ok(default_dir)
60+
}
61+
62+
/// 根据 artifact 获取目标路径
63+
/// 优先使用 install_path,如果为空则使用当前目录 + resource_name
64+
fn get_target_path(artifact: &Artifact) -> Result<PathBuf> {
65+
if let Some(ref install_path) = artifact.install_path {
66+
let path = PathBuf::from(install_path);
67+
68+
// 规范化路径,防止路径攻击
69+
let normalized_path = path.canonicalize().or_else(|_| {
70+
if path.components().any(|c| matches!(c, std::path::Component::ParentDir)) {
71+
return Err(anyhow::anyhow!("路径包含非法组件: {}", install_path));
72+
}
73+
Ok(path)
74+
})?;
75+
76+
// 使用服务器提供的 install_path
77+
Ok(normalized_path)
78+
} else {
79+
// 使用当前 exe 同级目录 + resource_name
80+
get_resource_path(&artifact.resource_name)
81+
}
82+
}
83+
84+
/// 检查资源文件是否存在且哈希匹配
85+
fn check_resource_file(artifact: &Artifact) -> Result<bool> {
86+
let target_path = get_target_path(artifact)?;
87+
88+
// 文件不存在,需要更新
89+
if !target_path.exists() {
90+
return Ok(true);
91+
}
92+
93+
// 计算本地文件哈希
94+
let local_hash = calculate_file_hash(&target_path)?;
95+
let remote_hash = artifact.hash.to_lowercase();
96+
97+
// 哈希不匹配,需要更新
98+
Ok(local_hash.to_lowercase() != remote_hash)
99+
}
100+
101+
/// 生成更新计划
102+
///
103+
/// # 参数
104+
/// - `check_response`: 服务器检查响应
105+
///
106+
/// # 返回
107+
/// 返回需要更新的任务列表
108+
pub fn plan_updates(check_response: &CheckResponse) -> Result<Vec<UpdateTask>> {
109+
let mut tasks = Vec::new();
110+
111+
// 遍历所有版本类型
112+
for (_version_type, artifacts) in &check_response.data.versions {
113+
for artifact in artifacts {
114+
// 检查是否需要更新
115+
let needs_update = check_resource_file(artifact)?;
116+
117+
if needs_update {
118+
// 获取目标路径(优先使用 install_path,为空则使用当前目录)
119+
let target_path = get_target_path(artifact)?;
120+
let exe_dir = target_path.parent().context("无法获取可执行文件目录")?;
121+
let temp_dir = get_temp_dir()?;
122+
let temp_path = temp_dir.join(format!("{}.tmp", artifact.resource_name));
123+
124+
tasks.push(UpdateTask {
125+
artifact: artifact.clone(),
126+
target_path: target_path.clone(),
127+
backup_path: Some(exe_dir.join(format!("{}.bak", artifact.resource_name))),
128+
temp_path,
129+
});
130+
}
131+
}
132+
}
133+
134+
Ok(tasks)
135+
}

0 commit comments

Comments
 (0)