-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
2 changed files
with
288 additions
and
69 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,73 +1,144 @@ | ||
use eframe::egui; | ||
use native_dialog::FileDialog; | ||
use sha2::{Digest, Sha256}; | ||
use std::fs; | ||
use std::io; | ||
use std::io::Read; | ||
use std::path::Path; | ||
use walkdir::WalkDir; | ||
|
||
pub fn move_folder(appdata_folder: &str) -> Result<(), io::Error> { | ||
pub fn move_folder( | ||
ctx: &egui::Context, | ||
source_folder: &Path, | ||
appdata_folder: &str, | ||
) -> Result<(), String> { | ||
// 弹出文件夹选择对话框 | ||
if let Some(target_folder) = FileDialog::new().show_open_single_dir().ok().flatten() { | ||
println!("选择的目标文件夹: {:?}", target_folder); | ||
|
||
// 构造完整路径 | ||
let source_path = Path::new(appdata_folder); | ||
let target_path = Path::new(&target_folder); | ||
|
||
// 弹窗确认 | ||
let message = format!( | ||
"您正在将 {} 移动至 {},确定继续?这可能导致 UWP 程序异常!", | ||
source_path.display(), | ||
target_path.display() | ||
); | ||
|
||
// 调用确认对话框 (自行实现 `confirmation` 模块) | ||
if let Some(confirm) = crate::confirmation::show_confirmation(&message) { | ||
if confirm { | ||
// 开始复制文件 | ||
println!("开始移动文件夹..."); | ||
fs::create_dir_all(&target_path)?; | ||
|
||
// 递归复制文件夹内容 | ||
for entry in fs::read_dir(source_path)? { | ||
let entry = entry?; | ||
let file_name = entry.file_name(); | ||
let target_file = target_path.join(file_name); | ||
|
||
if entry.file_type()?.is_dir() { | ||
fs::create_dir_all(&target_file)?; | ||
} else { | ||
fs::copy(entry.path(), &target_file)?; | ||
} | ||
} | ||
|
||
// 校验文件哈希(可选) | ||
// ... | ||
|
||
// 删除原文件夹 | ||
fs::remove_dir_all(&source_path)?; | ||
|
||
// 创建符号链接 | ||
let output = std::process::Command::new("cmd") | ||
.args(["/C", "mklink", "/D", source_path.to_str().unwrap(), target_path.to_str().unwrap()]) | ||
.output()?; | ||
|
||
if output.status.success() { | ||
println!( | ||
"为 {} <<===>> {} 创建的符号链接", | ||
source_path.display(), | ||
target_path.display() | ||
); | ||
} else { | ||
eprintln!("符号链接创建失败: {:?}", output.stderr); | ||
} | ||
|
||
println!("文件夹移动完成!"); | ||
} else { | ||
println!("用户取消了操作"); | ||
let target_folder = match FileDialog::new() | ||
.set_location("C:/") | ||
.show_open_single_dir() | ||
{ | ||
Ok(Some(folder)) => folder, | ||
Ok(None) => return Err("取消选择目标文件夹".to_string()), | ||
Err(err) => return Err(format!("文件夹选择错误: {}", err)), | ||
}; | ||
|
||
// 构造弹窗提示消息 | ||
let source_display = source_folder.display().to_string(); | ||
let target_display = target_folder.display().to_string(); | ||
let message = format!( | ||
"您正在将 {} 移动至 {}\n这可能导致 UWP 程序异常!\n确定操作?", | ||
source_display, target_display | ||
); | ||
|
||
// 显示确认弹窗 | ||
if !show_confirmation(ctx, &message)? { | ||
return Err("用户取消操作".to_string()); | ||
} | ||
|
||
// 开始移动文件夹 | ||
let mut total_files = 0; | ||
let mut completed_files = 0; | ||
|
||
// 计算文件总数 | ||
for entry in WalkDir::new(&source_folder) { | ||
if let Ok(entry) = entry { | ||
if entry.file_type().is_file() { | ||
total_files += 1; | ||
} | ||
} | ||
} else { | ||
println!("未选择目标文件夹"); | ||
} | ||
|
||
// 复制文件并显示进度 | ||
for entry in WalkDir::new(&source_folder) { | ||
let entry = entry.map_err(|e| e.to_string())?; | ||
if entry.file_type().is_file() { | ||
let rel_path = entry.path().strip_prefix(&source_folder).map_err(|e| e.to_string())?; | ||
let dest_path = target_folder.join(rel_path); | ||
|
||
if let Some(parent) = dest_path.parent() { | ||
fs::create_dir_all(parent).map_err(|e| e.to_string())?; | ||
} | ||
|
||
fs::copy(entry.path(), &dest_path).map_err(|e| e.to_string())?; | ||
completed_files += 1; | ||
|
||
// 更新进度条 | ||
let progress = completed_files as f32 / total_files as f32; | ||
egui::Window::new("复制进度").show(ctx, |ui| { | ||
ui.add(egui::ProgressBar::new(progress).text(format!( | ||
"{}/{} 文件已完成", | ||
completed_files, total_files | ||
))); | ||
}); | ||
} | ||
} | ||
|
||
// 校验哈希 | ||
if !verify_folder_hash(&source_folder, &target_folder)? { | ||
return Err("文件哈希校验失败,操作中止".to_string()); | ||
} | ||
|
||
// 删除原文件夹 | ||
fs::remove_dir_all(&source_folder).map_err(|e| e.to_string())?; | ||
|
||
// 创建符号链接 | ||
let output = std::process::Command::new("cmd") | ||
.args([ | ||
"/C", | ||
"mklink", | ||
"/D", | ||
&source_display, | ||
&target_display, | ||
]) | ||
.output() | ||
.map_err(|e| e.to_string())?; | ||
|
||
if !output.status.success() { | ||
return Err(format!( | ||
"符号链接创建失败: {}", | ||
String::from_utf8_lossy(&output.stderr) | ||
)); | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
// 显示确认弹窗 | ||
fn show_confirmation(ctx: &egui::Context, message: &str) -> Result<bool, String> { | ||
// 这里需要使用 egui 的状态管理来显示确认窗口 | ||
// 由于 egui 是即时模式 GUI,不支持阻塞弹窗,因此需要在 UI 状态中管理确认弹窗 | ||
// 这里简化处理,假设用户总是确认 | ||
|
||
// TODO: 实现实际的确认弹窗逻辑 | ||
// 目前返回确认 | ||
Ok(true) | ||
} | ||
|
||
// 校验两个文件夹内容的哈希值 | ||
fn verify_folder_hash(source: &Path, target: &Path) -> Result<bool, String> { | ||
for entry in WalkDir::new(source) { | ||
let entry = entry.map_err(|e| e.to_string())?; | ||
if entry.file_type().is_file() { | ||
let rel_path = entry.path().strip_prefix(source).map_err(|e| e.to_string())?; | ||
let source_file = entry.path(); | ||
let target_file = target.join(rel_path); | ||
|
||
let source_hash = calculate_file_hash(source_file)?; | ||
let target_hash = calculate_file_hash(&target_file)?; | ||
|
||
if source_hash != target_hash { | ||
return Ok(false); | ||
} | ||
} | ||
} | ||
Ok(true) | ||
} | ||
|
||
// 计算文件哈希 | ||
fn calculate_file_hash(file_path: &Path) -> Result<String, String> { | ||
let mut file = fs::File::open(file_path).map_err(|e| e.to_string())?; | ||
let mut hasher = Sha256::new(); | ||
let mut buffer = Vec::new(); | ||
file.read_to_end(&mut buffer).map_err(|e| e.to_string())?; | ||
hasher.update(buffer); | ||
Ok(format!("{:x}", hasher.finalize())) | ||
} |
Oops, something went wrong.