refactor backend
This commit is contained in:
61
src-tauri/src/backend/advanced_clean.rs
Normal file
61
src-tauri/src/backend/advanced_clean.rs
Normal file
@@ -0,0 +1,61 @@
|
||||
use std::fs;
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
pub async fn run_dism_cleanup() -> Result<String, String> {
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
let output = Command::new("dism.exe")
|
||||
.args(["/online", "/Cleanup-Image", "/StartComponentCleanup"])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.output()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok("系统组件清理完成。".into())
|
||||
} else {
|
||||
Err(String::from_utf8_lossy(&output.stderr).to_string())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn clean_thumbnails() -> Result<String, String> {
|
||||
let local_app_data = std::env::var("LOCALAPPDATA").map_err(|_| "无法获取 LocalAppData 路径")?;
|
||||
let thumb_path = Path::new(&local_app_data).join("Microsoft\\Windows\\Explorer");
|
||||
|
||||
let mut count = 0;
|
||||
if thumb_path.exists() {
|
||||
if let Ok(entries) = fs::read_dir(thumb_path) {
|
||||
for entry in entries.filter_map(|e| e.ok()) {
|
||||
let name = entry.file_name().to_string_lossy().to_lowercase();
|
||||
if name.starts_with("thumbcache_") && name.ends_with(".db") {
|
||||
if fs::remove_file(entry.path()).is_ok() {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
Ok(format!("成功清理 {} 个缩略图缓存文件。", count))
|
||||
} else {
|
||||
Ok("未发现可清理的缩略图缓存,或文件正被系统占用。".into())
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn disable_hibernation() -> Result<String, String> {
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
let output = Command::new("powercfg.exe")
|
||||
.args(["-h", "off"])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.output()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok("休眠模式已关闭,hiberfil.sys 已移除。".into())
|
||||
} else {
|
||||
Err("执行失败,请确保以管理员身份运行。".into())
|
||||
}
|
||||
}
|
||||
109
src-tauri/src/backend/browser_clean.rs
Normal file
109
src-tauri/src/backend/browser_clean.rs
Normal file
@@ -0,0 +1,109 @@
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::backend::fast_clean::clean_directory_contents;
|
||||
use crate::backend::models::{BrowserProfile, BrowserScanResult, BrowserType, CleanResult};
|
||||
use crate::backend::utils::{format_size, get_dir_size_simple};
|
||||
|
||||
const BROWSER_CACHE_DIRS: &[&str] = &[
|
||||
"Cache",
|
||||
"Code Cache",
|
||||
"GPUCache",
|
||||
"Media Cache",
|
||||
"Service Worker/CacheStorage",
|
||||
"Service Worker/ScriptCache",
|
||||
"GrShaderCache",
|
||||
"DawnCache",
|
||||
"File System",
|
||||
"blob_storage",
|
||||
];
|
||||
|
||||
impl BrowserType {
|
||||
fn get_user_data_path(&self) -> Result<PathBuf, String> {
|
||||
let local_app_data = std::env::var("LOCALAPPDATA").map_err(|_| "无法获取 LocalAppData 路径")?;
|
||||
let base = Path::new(&local_app_data);
|
||||
|
||||
match self {
|
||||
BrowserType::Chrome => Ok(base.join("Google\\Chrome\\User Data")),
|
||||
BrowserType::Edge => Ok(base.join("Microsoft\\Edge\\User Data")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_browser_scan(browser: BrowserType) -> Result<BrowserScanResult, String> {
|
||||
let user_data_path = browser.get_user_data_path()?;
|
||||
let local_state_path = user_data_path.join("Local State");
|
||||
|
||||
let mut profiles = Vec::new();
|
||||
let mut total_bytes = 0;
|
||||
|
||||
if local_state_path.exists() {
|
||||
let content = fs::read_to_string(local_state_path).map_err(|e| e.to_string())?;
|
||||
let value: serde_json::Value = serde_json::from_str(&content).map_err(|e| e.to_string())?;
|
||||
|
||||
if let Some(info_cache) = value
|
||||
.get("profile")
|
||||
.and_then(|profile| profile.get("info_cache"))
|
||||
.and_then(|info| info.as_object())
|
||||
{
|
||||
for (dir_name, info) in info_cache {
|
||||
let profile_display_name = info.get("name").and_then(|name| name.as_str()).unwrap_or(dir_name);
|
||||
let profile_path = user_data_path.join(dir_name);
|
||||
|
||||
if profile_path.exists() {
|
||||
let mut size = 0;
|
||||
for sub_dir in BROWSER_CACHE_DIRS {
|
||||
let target = profile_path.join(sub_dir);
|
||||
if target.exists() {
|
||||
size += get_dir_size_simple(&target);
|
||||
}
|
||||
}
|
||||
|
||||
total_bytes += size;
|
||||
profiles.push(BrowserProfile {
|
||||
name: profile_display_name.to_string(),
|
||||
path_name: dir_name.clone(),
|
||||
cache_size: size,
|
||||
cache_size_str: format_size(size),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(BrowserScanResult {
|
||||
profiles,
|
||||
total_size: format_size(total_bytes),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn run_browser_clean(
|
||||
browser: BrowserType,
|
||||
profile_paths: Vec<String>,
|
||||
) -> Result<CleanResult, String> {
|
||||
let user_data_path = browser.get_user_data_path()?;
|
||||
let mut total_freed = 0;
|
||||
let mut success_count = 0;
|
||||
let mut fail_count = 0;
|
||||
|
||||
for profile_dir in profile_paths {
|
||||
let profile_path = user_data_path.join(&profile_dir);
|
||||
if profile_path.exists() {
|
||||
for sub_dir in BROWSER_CACHE_DIRS {
|
||||
let target = profile_path.join(sub_dir);
|
||||
if target.exists() {
|
||||
let (freed, success, fail) = clean_directory_contents(&target, None);
|
||||
total_freed += freed;
|
||||
success_count += success;
|
||||
fail_count += fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CleanResult {
|
||||
total_freed: format_size(total_freed),
|
||||
success_count,
|
||||
fail_count,
|
||||
})
|
||||
}
|
||||
103
src-tauri/src/backend/disk_analysis.rs
Normal file
103
src-tauri/src/backend/disk_analysis.rs
Normal file
@@ -0,0 +1,103 @@
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::os::windows::process::CommandExt;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
|
||||
use tauri::Emitter;
|
||||
|
||||
use crate::backend::models::{FileTreeNode, ScanProgress};
|
||||
use crate::backend::state::DiskState;
|
||||
use crate::backend::utils::format_size;
|
||||
|
||||
pub async fn run_full_scan(root_path: String, state: &DiskState, app_handle: tauri::AppHandle) {
|
||||
use jwalk::WalkDir;
|
||||
|
||||
let mut dir_sizes = HashMap::new();
|
||||
let root = Path::new(&root_path);
|
||||
let mut file_count = 0;
|
||||
|
||||
for entry in WalkDir::new(root).skip_hidden(false).into_iter().filter_map(|e| e.ok()) {
|
||||
if entry.file_type.is_file() {
|
||||
file_count += 1;
|
||||
|
||||
if file_count % 2000 == 0 {
|
||||
let _ = app_handle.emit(
|
||||
"scan-progress",
|
||||
ScanProgress {
|
||||
file_count,
|
||||
current_path: entry.parent_path().to_string_lossy().to_string(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
let size = entry.metadata().map(|m| m.len()).unwrap_or(0);
|
||||
let mut current_path = entry.parent_path().to_path_buf();
|
||||
while current_path.starts_with(root) {
|
||||
let path_str = current_path.to_string_lossy().to_string();
|
||||
*dir_sizes.entry(path_str).or_insert(0) += size;
|
||||
if current_path == root {
|
||||
break;
|
||||
}
|
||||
if let Some(parent) = current_path.parent() {
|
||||
current_path = parent.to_path_buf();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut state_dirs = state.dir_sizes.lock().unwrap();
|
||||
*state_dirs = dir_sizes;
|
||||
}
|
||||
|
||||
pub fn get_children(parent_path: String, state: &DiskState) -> Vec<FileTreeNode> {
|
||||
let dir_sizes = state.dir_sizes.lock().unwrap();
|
||||
let mut results = Vec::new();
|
||||
let parent_size = *dir_sizes.get(&parent_path).unwrap_or(&1);
|
||||
|
||||
if let Ok(entries) = fs::read_dir(Path::new(&parent_path)) {
|
||||
for entry in entries.filter_map(|e| e.ok()) {
|
||||
let path = entry.path();
|
||||
let path_str = path.to_string_lossy().to_string();
|
||||
let is_dir = path.is_dir();
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
|
||||
let size = if is_dir {
|
||||
*dir_sizes.get(&path_str).unwrap_or(&0)
|
||||
} else {
|
||||
entry.metadata().map(|m| m.len()).unwrap_or(0)
|
||||
};
|
||||
|
||||
if size > 0 || !is_dir {
|
||||
results.push(FileTreeNode {
|
||||
name,
|
||||
path: path_str,
|
||||
is_dir,
|
||||
size,
|
||||
size_str: format_size(size),
|
||||
percent: (size as f64 / parent_size as f64 * 100.0) as f32,
|
||||
file_count: 0,
|
||||
has_children: is_dir,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
results.sort_by(|a, b| b.size.cmp(&a.size));
|
||||
results
|
||||
}
|
||||
|
||||
pub async fn open_explorer(path: String) -> Result<(), String> {
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
|
||||
Command::new("explorer.exe")
|
||||
.arg("/select,")
|
||||
.arg(&path)
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.spawn()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
161
src-tauri/src/backend/fast_clean.rs
Normal file
161
src-tauri/src/backend/fast_clean.rs
Normal file
@@ -0,0 +1,161 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::time::{Duration, SystemTime};
|
||||
|
||||
use crate::backend::models::{CleanResult, CleaningConfig, FastScanResult, ScanItem};
|
||||
use crate::backend::utils::format_size;
|
||||
|
||||
fn get_fast_cleaning_configs() -> Vec<CleaningConfig> {
|
||||
let mut configs = Vec::new();
|
||||
|
||||
if let Ok(temp) = std::env::var("TEMP") {
|
||||
configs.push(CleaningConfig::new("用户临时文件", &temp, None, true));
|
||||
}
|
||||
|
||||
configs.push(CleaningConfig::new("系统临时文件", "C:\\Windows\\Temp", None, true));
|
||||
configs.push(CleaningConfig::new(
|
||||
"Windows 更新残留",
|
||||
"C:\\Windows\\SoftwareDistribution\\Download",
|
||||
Some(10),
|
||||
true,
|
||||
));
|
||||
configs.push(CleaningConfig::new(
|
||||
"内核转储文件",
|
||||
"C:\\Windows\\LiveKernelReports",
|
||||
None,
|
||||
false,
|
||||
));
|
||||
|
||||
configs
|
||||
}
|
||||
|
||||
pub async fn run_fast_scan() -> FastScanResult {
|
||||
let configs = get_fast_cleaning_configs();
|
||||
let mut items = Vec::new();
|
||||
let mut total_bytes = 0;
|
||||
let mut total_count = 0;
|
||||
|
||||
for config in configs {
|
||||
let (size, count) = get_dir_stats(Path::new(&config.path), config.filter_days);
|
||||
items.push(ScanItem {
|
||||
name: config.name,
|
||||
path: config.path,
|
||||
size,
|
||||
count,
|
||||
enabled: config.default_enabled,
|
||||
});
|
||||
total_bytes += size;
|
||||
total_count += count;
|
||||
}
|
||||
|
||||
FastScanResult {
|
||||
items,
|
||||
total_size: format_size(total_bytes),
|
||||
total_count,
|
||||
}
|
||||
}
|
||||
|
||||
fn get_dir_stats(path: &Path, filter_days: Option<u64>) -> (u64, u32) {
|
||||
if !path.exists() {
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
let mut size = 0;
|
||||
let mut count = 0;
|
||||
let now = SystemTime::now();
|
||||
let dur = filter_days.map(|days| Duration::from_secs(days * 24 * 3600));
|
||||
|
||||
for entry in walkdir::WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
|
||||
if entry.file_type().is_file() {
|
||||
let mut allowed = true;
|
||||
if let (Some(filter_duration), Ok(metadata)) = (dur, entry.metadata()) {
|
||||
if let Ok(modified_time) = metadata.modified() {
|
||||
if let Ok(elapsed) = now.duration_since(modified_time) {
|
||||
if elapsed < filter_duration {
|
||||
allowed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if allowed {
|
||||
size += entry.metadata().map(|m| m.len()).unwrap_or(0);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(size, count)
|
||||
}
|
||||
|
||||
pub async fn run_fast_clean(selected_paths: Vec<String>) -> Result<CleanResult, String> {
|
||||
let configs = get_fast_cleaning_configs();
|
||||
let mut success_count = 0;
|
||||
let mut fail_count = 0;
|
||||
let mut total_freed = 0;
|
||||
|
||||
for config in configs {
|
||||
if selected_paths.contains(&config.path) {
|
||||
let path = Path::new(&config.path);
|
||||
if path.exists() {
|
||||
let (freed, success, fail) = clean_directory_contents(path, config.filter_days);
|
||||
total_freed += freed;
|
||||
success_count += success;
|
||||
fail_count += fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CleanResult {
|
||||
total_freed: format_size(total_freed),
|
||||
success_count,
|
||||
fail_count,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn clean_directory_contents(path: &Path, filter_days: Option<u64>) -> (u64, u32, u32) {
|
||||
let mut freed = 0;
|
||||
let mut success = 0;
|
||||
let mut fail = 0;
|
||||
let now = SystemTime::now();
|
||||
let dur = filter_days.map(|days| Duration::from_secs(days * 24 * 3600));
|
||||
|
||||
if let Ok(entries) = fs::read_dir(path) {
|
||||
for entry in entries.filter_map(|e| e.ok()) {
|
||||
let entry_path = entry.path();
|
||||
let metadata = entry.metadata();
|
||||
let size = metadata.as_ref().map(|m| m.len()).unwrap_or(0);
|
||||
|
||||
if let (Some(filter_duration), Ok(metadata)) = (dur, &metadata) {
|
||||
if let Ok(modified_time) = metadata.modified() {
|
||||
if let Ok(elapsed) = now.duration_since(modified_time) {
|
||||
if elapsed < filter_duration {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if entry_path.is_file() {
|
||||
if fs::remove_file(&entry_path).is_ok() {
|
||||
freed += size;
|
||||
success += 1;
|
||||
} else {
|
||||
fail += 1;
|
||||
}
|
||||
} else if entry_path.is_dir() {
|
||||
let (dir_freed, dir_success, dir_fail) =
|
||||
clean_directory_contents(&entry_path, filter_days);
|
||||
freed += dir_freed;
|
||||
success += dir_success;
|
||||
fail += dir_fail;
|
||||
|
||||
if fs::remove_dir(&entry_path).is_ok() {
|
||||
success += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(freed, success, fail)
|
||||
}
|
||||
62
src-tauri/src/backend/memory_clean.rs
Normal file
62
src-tauri/src/backend/memory_clean.rs
Normal file
@@ -0,0 +1,62 @@
|
||||
use sysinfo::{ProcessesToUpdate, System};
|
||||
|
||||
use crate::backend::models::MemoryStats;
|
||||
|
||||
pub fn get_memory_stats() -> MemoryStats {
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_memory();
|
||||
|
||||
let total = sys.total_memory();
|
||||
let used = sys.used_memory();
|
||||
let free = total.saturating_sub(used);
|
||||
let percent = (used as f32 / total as f32) * 100.0;
|
||||
|
||||
MemoryStats {
|
||||
total,
|
||||
used,
|
||||
free,
|
||||
percent,
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_memory_clean() -> Result<u64, String> {
|
||||
use windows_sys::Win32::Foundation::CloseHandle;
|
||||
use windows_sys::Win32::System::ProcessStatus::EmptyWorkingSet;
|
||||
use windows_sys::Win32::System::Threading::{
|
||||
OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_SET_QUOTA,
|
||||
};
|
||||
|
||||
let before = get_memory_stats().used;
|
||||
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_processes(ProcessesToUpdate::All, true);
|
||||
|
||||
for (pid, _) in sys.processes() {
|
||||
let pid_u32 = pid.as_u32();
|
||||
unsafe {
|
||||
let handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_SET_QUOTA, 0, pid_u32);
|
||||
if handle != std::ptr::null_mut() {
|
||||
EmptyWorkingSet(handle);
|
||||
CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
||||
|
||||
let after = get_memory_stats().used;
|
||||
Ok(before.saturating_sub(after))
|
||||
}
|
||||
|
||||
pub async fn run_deep_memory_clean() -> Result<u64, String> {
|
||||
use windows_sys::Win32::System::Memory::SetSystemFileCacheSize;
|
||||
|
||||
let before = get_memory_stats().used;
|
||||
|
||||
unsafe {
|
||||
SetSystemFileCacheSize(usize::MAX, usize::MAX, 0);
|
||||
}
|
||||
|
||||
let after = get_memory_stats().used;
|
||||
Ok(before.saturating_sub(after))
|
||||
}
|
||||
8
src-tauri/src/backend/mod.rs
Normal file
8
src-tauri/src/backend/mod.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
pub mod advanced_clean;
|
||||
pub mod browser_clean;
|
||||
pub mod disk_analysis;
|
||||
pub mod fast_clean;
|
||||
pub mod memory_clean;
|
||||
pub mod models;
|
||||
pub mod state;
|
||||
pub mod utils;
|
||||
88
src-tauri/src/backend/models.rs
Normal file
88
src-tauri/src/backend/models.rs
Normal file
@@ -0,0 +1,88 @@
|
||||
use serde::Serialize;
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct FileTreeNode {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub is_dir: bool,
|
||||
pub size: u64,
|
||||
pub size_str: String,
|
||||
pub percent: f32,
|
||||
pub file_count: u32,
|
||||
pub has_children: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct ScanProgress {
|
||||
pub file_count: u64,
|
||||
pub current_path: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CleaningConfig {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub filter_days: Option<u64>,
|
||||
pub default_enabled: bool,
|
||||
}
|
||||
|
||||
impl CleaningConfig {
|
||||
pub fn new(name: &str, path: &str, filter_days: Option<u64>, default_enabled: bool) -> Self {
|
||||
Self {
|
||||
name: name.into(),
|
||||
path: path.into(),
|
||||
filter_days,
|
||||
default_enabled,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct ScanItem {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub size: u64,
|
||||
pub count: u32,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct FastScanResult {
|
||||
pub items: Vec<ScanItem>,
|
||||
pub total_size: String,
|
||||
pub total_count: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct CleanResult {
|
||||
pub total_freed: String,
|
||||
pub success_count: u32,
|
||||
pub fail_count: u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct BrowserProfile {
|
||||
pub name: String,
|
||||
pub path_name: String,
|
||||
pub cache_size: u64,
|
||||
pub cache_size_str: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct BrowserScanResult {
|
||||
pub profiles: Vec<BrowserProfile>,
|
||||
pub total_size: String,
|
||||
}
|
||||
|
||||
pub enum BrowserType {
|
||||
Chrome,
|
||||
Edge,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct MemoryStats {
|
||||
pub total: u64,
|
||||
pub used: u64,
|
||||
pub free: u64,
|
||||
pub percent: f32,
|
||||
}
|
||||
6
src-tauri/src/backend/state.rs
Normal file
6
src-tauri/src/backend/state.rs
Normal file
@@ -0,0 +1,6 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub struct DiskState {
|
||||
pub dir_sizes: Mutex<HashMap<String, u64>>,
|
||||
}
|
||||
26
src-tauri/src/backend/utils.rs
Normal file
26
src-tauri/src/backend/utils.rs
Normal file
@@ -0,0 +1,26 @@
|
||||
use std::path::Path;
|
||||
|
||||
pub fn format_size(size: u64) -> String {
|
||||
const KB: u64 = 1024;
|
||||
const MB: u64 = KB * 1024;
|
||||
const GB: u64 = MB * 1024;
|
||||
|
||||
if size >= GB {
|
||||
format!("{:.2} GB", size as f64 / GB as f64)
|
||||
} else if size >= MB {
|
||||
format!("{:.2} MB", size as f64 / MB as f64)
|
||||
} else if size >= KB {
|
||||
format!("{:.2} KB", size as f64 / KB as f64)
|
||||
} else {
|
||||
format!("{} B", size)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_dir_size_simple(path: &Path) -> u64 {
|
||||
walkdir::WalkDir::new(path)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.file_type().is_file())
|
||||
.map(|e| e.metadata().map(|m| m.len()).unwrap_or(0))
|
||||
.sum()
|
||||
}
|
||||
@@ -1,565 +0,0 @@
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
use std::time::{SystemTime, Duration};
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use std::process::Command;
|
||||
use std::os::windows::process::CommandExt;
|
||||
|
||||
// 存储全盘扫描后的结果
|
||||
pub struct DiskState {
|
||||
pub dir_sizes: Mutex<HashMap<String, u64>>,
|
||||
// pub file_info: Mutex<HashMap<String, (u64, u32)>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct FileTreeNode {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub is_dir: bool,
|
||||
pub size: u64,
|
||||
pub size_str: String,
|
||||
pub percent: f32,
|
||||
pub file_count: u32,
|
||||
pub has_children: bool,
|
||||
}
|
||||
|
||||
pub fn format_size(size: u64) -> String {
|
||||
const KB: u64 = 1024;
|
||||
const MB: u64 = KB * 1024;
|
||||
const GB: u64 = MB * 1024;
|
||||
if size >= GB { format!("{:.2} GB", size as f64 / GB as f64) }
|
||||
else if size >= MB { format!("{:.2} MB", size as f64 / MB as f64) }
|
||||
else if size >= KB { format!("{:.2} KB", size as f64 / KB as f64) }
|
||||
else { format!("{} B", size) }
|
||||
}
|
||||
|
||||
// --- 高级清理功能实现 ---
|
||||
|
||||
// 1. 系统组件清理 (DISM)
|
||||
pub async fn run_dism_cleanup() -> Result<String, String> {
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
let output = Command::new("dism.exe")
|
||||
.args(&["/online", "/Cleanup-Image", "/StartComponentCleanup"])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.output()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok("系统组件清理完成。".into())
|
||||
} else {
|
||||
Err(String::from_utf8_lossy(&output.stderr).to_string())
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 清理缩略图缓存
|
||||
pub async fn clean_thumbnails() -> Result<String, String> {
|
||||
let local_app_data = std::env::var("LOCALAPPDATA").map_err(|_| "无法获取 LocalAppData 路径")?;
|
||||
let thumb_path = Path::new(&local_app_data).join("Microsoft\\Windows\\Explorer");
|
||||
|
||||
let mut count = 0;
|
||||
if thumb_path.exists() {
|
||||
if let Ok(entries) = fs::read_dir(thumb_path) {
|
||||
for entry in entries.filter_map(|e| e.ok()) {
|
||||
let name = entry.file_name().to_string_lossy().to_lowercase();
|
||||
if name.starts_with("thumbcache_") && name.ends_with(".db") {
|
||||
// 缩略图文件通常被 Explorer 占用,这里尝试删除,失败也继续
|
||||
if fs::remove_file(entry.path()).is_ok() {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if count > 0 {
|
||||
Ok(format!("成功清理 {} 个缩略图缓存文件。", count))
|
||||
} else {
|
||||
Ok("未发现可清理的缩略图缓存,或文件正被系统占用。".into())
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 关闭休眠文件
|
||||
pub async fn disable_hibernation() -> Result<String, String> {
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
let output = Command::new("powercfg.exe")
|
||||
.args(&["-h", "off"])
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.output()
|
||||
.map_err(|e| e.to_string())?;
|
||||
|
||||
if output.status.success() {
|
||||
Ok("休眠模式已关闭,hiberfil.sys 已移除。".into())
|
||||
} else {
|
||||
Err("执行失败,请确保以管理员身份运行。".into())
|
||||
}
|
||||
}
|
||||
|
||||
// --- 原有逻辑保持 (磁盘树等) ---
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct ScanProgress {
|
||||
pub file_count: u64,
|
||||
pub current_path: String,
|
||||
}
|
||||
|
||||
pub async fn run_full_scan(root_path: String, state: &DiskState, app_handle: tauri::AppHandle) {
|
||||
use jwalk::WalkDir;
|
||||
use tauri::Emitter;
|
||||
|
||||
let mut dir_sizes = HashMap::new();
|
||||
let root = Path::new(&root_path);
|
||||
let mut file_count = 0;
|
||||
|
||||
for entry in WalkDir::new(root).skip_hidden(false).into_iter().filter_map(|e| e.ok()) {
|
||||
if entry.file_type.is_file() {
|
||||
file_count += 1;
|
||||
|
||||
// 节流推送进度:每 2000 个文件推送一次,避免 IPC 过载
|
||||
if file_count % 2000 == 0 {
|
||||
let _ = app_handle.emit("scan-progress", ScanProgress {
|
||||
file_count,
|
||||
current_path: entry.parent_path().to_string_lossy().to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
let size = entry.metadata().map(|m| m.len()).unwrap_or(0);
|
||||
let mut current_path = entry.parent_path().to_path_buf();
|
||||
while current_path.starts_with(root) {
|
||||
let path_str = current_path.to_string_lossy().to_string();
|
||||
*dir_sizes.entry(path_str).or_insert(0) += size;
|
||||
if current_path == root { break; }
|
||||
if let Some(parent) = current_path.parent() { current_path = parent.to_path_buf(); } else { break; }
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut state_dirs = state.dir_sizes.lock().unwrap();
|
||||
*state_dirs = dir_sizes;
|
||||
}
|
||||
|
||||
pub fn get_children(parent_path: String, state: &DiskState) -> Vec<FileTreeNode> {
|
||||
let dir_sizes = state.dir_sizes.lock().unwrap();
|
||||
let mut results = Vec::new();
|
||||
let parent_size = *dir_sizes.get(&parent_path).unwrap_or(&1);
|
||||
|
||||
if let Ok(entries) = fs::read_dir(Path::new(&parent_path)) {
|
||||
for entry in entries.filter_map(|e| e.ok()) {
|
||||
let path = entry.path();
|
||||
let path_str = path.to_string_lossy().to_string();
|
||||
let is_dir = path.is_dir();
|
||||
let name = entry.file_name().to_string_lossy().to_string();
|
||||
|
||||
let size = if is_dir {
|
||||
*dir_sizes.get(&path_str).unwrap_or(&0)
|
||||
} else {
|
||||
entry.metadata().map(|m| m.len()).unwrap_or(0)
|
||||
};
|
||||
|
||||
if size > 0 || !is_dir {
|
||||
results.push(FileTreeNode {
|
||||
name,
|
||||
path: path_str,
|
||||
is_dir,
|
||||
size,
|
||||
size_str: format_size(size),
|
||||
percent: (size as f64 / parent_size as f64 * 100.0) as f32,
|
||||
file_count: 0,
|
||||
has_children: is_dir,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
results.sort_by(|a, b| b.size.cmp(&a.size));
|
||||
results
|
||||
}
|
||||
|
||||
pub async fn open_explorer(path: String) -> Result<(), String> {
|
||||
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||
// 使用 /select, 参数可以在打开目录的同时选中目标
|
||||
Command::new("explorer.exe")
|
||||
.arg("/select,")
|
||||
.arg(&path)
|
||||
.creation_flags(CREATE_NO_WINDOW)
|
||||
.spawn()
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// --- 快速模式配置与逻辑 ---
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CleaningConfig {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub filter_days: Option<u64>,
|
||||
pub default_enabled: bool,
|
||||
}
|
||||
|
||||
impl CleaningConfig {
|
||||
fn new(name: &str, path: &str, filter_days: Option<u64>, default_enabled: bool) -> Self {
|
||||
Self { name: name.into(), path: path.into(), filter_days, default_enabled }
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取当前所有快速清理项的配置
|
||||
fn get_fast_cleaning_configs() -> Vec<CleaningConfig> {
|
||||
let mut configs = Vec::new();
|
||||
|
||||
// 1. 用户临时文件
|
||||
if let Ok(t) = std::env::var("TEMP") {
|
||||
configs.push(CleaningConfig::new("用户临时文件", &t, None, true));
|
||||
}
|
||||
|
||||
// 2. 系统临时文件
|
||||
configs.push(CleaningConfig::new("系统临时文件", "C:\\Windows\\Temp", None, true));
|
||||
|
||||
// 3. Windows 更新残留 (通常建议清理 10 天前的)
|
||||
configs.push(CleaningConfig::new("Windows 更新残留", "C:\\Windows\\SoftwareDistribution\\Download", Some(10), true));
|
||||
|
||||
// 4. 内核转储文件
|
||||
configs.push(CleaningConfig::new("内核转储文件", "C:\\Windows\\LiveKernelReports", None, false));
|
||||
|
||||
configs
|
||||
}
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct ScanItem {
|
||||
pub name: String,
|
||||
pub path: String,
|
||||
pub size: u64,
|
||||
pub count: u32,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct FastScanResult { pub items: Vec<ScanItem>, total_size: String, total_count: u32 }
|
||||
|
||||
pub async fn run_fast_scan() -> FastScanResult {
|
||||
let configs = get_fast_cleaning_configs();
|
||||
let mut items = Vec::new();
|
||||
let mut total_bytes = 0;
|
||||
let mut total_count = 0;
|
||||
|
||||
for config in configs {
|
||||
let (size, count) = get_dir_stats(Path::new(&config.path), config.filter_days);
|
||||
items.push(ScanItem {
|
||||
name: config.name,
|
||||
path: config.path,
|
||||
size,
|
||||
count,
|
||||
enabled: config.default_enabled,
|
||||
});
|
||||
total_bytes += size;
|
||||
total_count += count;
|
||||
}
|
||||
|
||||
FastScanResult {
|
||||
items,
|
||||
total_size: format_size(total_bytes),
|
||||
total_count
|
||||
}
|
||||
}
|
||||
|
||||
fn get_dir_stats(path: &Path, filter_days: Option<u64>) -> (u64, u32) {
|
||||
if !path.exists() { return (0, 0); }
|
||||
let mut size = 0;
|
||||
let mut count = 0;
|
||||
let now = SystemTime::now();
|
||||
let dur = filter_days.map(|d| Duration::from_secs(d * 24 * 3600));
|
||||
|
||||
for entry in walkdir::WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
|
||||
if entry.file_type().is_file() {
|
||||
let mut ok = true;
|
||||
if let (Some(d), Ok(m)) = (dur, entry.metadata()) {
|
||||
if let Ok(mod_t) = m.modified() {
|
||||
if let Ok(el) = now.duration_since(mod_t) {
|
||||
if el < d { ok = false; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if ok {
|
||||
size += entry.metadata().map(|m| m.len()).unwrap_or(0);
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
(size, count)
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct CleanResult {
|
||||
pub total_freed: String,
|
||||
pub success_count: u32,
|
||||
pub fail_count: u32,
|
||||
}
|
||||
|
||||
pub async fn run_fast_clean(selected_paths: Vec<String>) -> Result<CleanResult, String> {
|
||||
let configs = get_fast_cleaning_configs();
|
||||
let mut success_count = 0;
|
||||
let mut fail_count = 0;
|
||||
let mut total_freed: u64 = 0;
|
||||
|
||||
for config in configs {
|
||||
if selected_paths.contains(&config.path) {
|
||||
let path = Path::new(&config.path);
|
||||
if path.exists() {
|
||||
let (freed, s, f) = clean_directory_contents(path, config.filter_days);
|
||||
total_freed += freed;
|
||||
success_count += s;
|
||||
fail_count += f;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CleanResult {
|
||||
total_freed: format_size(total_freed),
|
||||
success_count,
|
||||
fail_count,
|
||||
})
|
||||
}
|
||||
|
||||
fn clean_directory_contents(path: &Path, filter_days: Option<u64>) -> (u64, u32, u32) {
|
||||
let mut freed = 0;
|
||||
let mut success = 0;
|
||||
let mut fail = 0;
|
||||
let now = SystemTime::now();
|
||||
let dur = filter_days.map(|d| Duration::from_secs(d * 24 * 3600));
|
||||
|
||||
if let Ok(entries) = fs::read_dir(path) {
|
||||
for entry in entries.filter_map(|e| e.ok()) {
|
||||
let entry_path = entry.path();
|
||||
let metadata = entry.metadata();
|
||||
let size = metadata.as_ref().map(|m| m.len()).unwrap_or(0);
|
||||
|
||||
// 检查过滤逻辑 (如果设置了天数)
|
||||
if let (Some(d), Ok(m)) = (dur, &metadata) {
|
||||
if let Ok(mod_t) = m.modified() {
|
||||
if let Ok(el) = now.duration_since(mod_t) {
|
||||
if el < d { continue; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if entry_path.is_file() {
|
||||
if fs::remove_file(&entry_path).is_ok() {
|
||||
freed += size;
|
||||
success += 1;
|
||||
} else {
|
||||
fail += 1;
|
||||
}
|
||||
} else if entry_path.is_dir() {
|
||||
// 递归清理子目录
|
||||
let (f, s, fl) = clean_directory_contents(&entry_path, filter_days);
|
||||
freed += f;
|
||||
success += s;
|
||||
fail += fl;
|
||||
// 尝试删除已清空的目录 (如果它本身不是根清理目录且已过期)
|
||||
if fs::remove_dir(&entry_path).is_ok() {
|
||||
success += 1;
|
||||
} else {
|
||||
// 目录可能因为包含未过期的文件而无法删除,这是正常的
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
(freed, success, fail)
|
||||
}
|
||||
|
||||
// --- 浏览器清理逻辑 ---
|
||||
|
||||
const BROWSER_CACHE_DIRS: &[&str] = &[
|
||||
"Cache",
|
||||
"Code Cache",
|
||||
"GPUCache",
|
||||
"Media Cache",
|
||||
"Service Worker/CacheStorage",
|
||||
"Service Worker/ScriptCache",
|
||||
"GrShaderCache",
|
||||
"DawnCache",
|
||||
"File System",
|
||||
"blob_storage"
|
||||
];
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct BrowserProfile {
|
||||
pub name: String,
|
||||
pub path_name: String,
|
||||
pub cache_size: u64,
|
||||
pub cache_size_str: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
pub struct BrowserScanResult {
|
||||
pub profiles: Vec<BrowserProfile>,
|
||||
pub total_size: String,
|
||||
}
|
||||
|
||||
pub enum BrowserType {
|
||||
Chrome,
|
||||
Edge,
|
||||
}
|
||||
|
||||
impl BrowserType {
|
||||
fn get_user_data_path(&self) -> Result<std::path::PathBuf, String> {
|
||||
let local_app_data = std::env::var("LOCALAPPDATA").map_err(|_| "无法获取 LocalAppData 路径")?;
|
||||
let base = std::path::Path::new(&local_app_data);
|
||||
match self {
|
||||
BrowserType::Chrome => Ok(base.join("Google\\Chrome\\User Data")),
|
||||
BrowserType::Edge => Ok(base.join("Microsoft\\Edge\\User Data")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn run_browser_scan(browser: BrowserType) -> Result<BrowserScanResult, String> {
|
||||
let user_data_path = browser.get_user_data_path()?;
|
||||
let local_state_path = user_data_path.join("Local State");
|
||||
|
||||
let mut profiles = Vec::new();
|
||||
let mut total_bytes = 0;
|
||||
|
||||
if local_state_path.exists() {
|
||||
let content = fs::read_to_string(local_state_path).map_err(|e| e.to_string())?;
|
||||
let v: serde_json::Value = serde_json::from_str(&content).map_err(|e| e.to_string())?;
|
||||
|
||||
if let Some(info_cache) = v.get("profile").and_then(|p| p.get("info_cache")).and_then(|i| i.as_object()) {
|
||||
for (dir_name, info) in info_cache {
|
||||
let profile_display_name = info.get("name").and_then(|n| n.as_str()).unwrap_or(dir_name);
|
||||
let profile_path = user_data_path.join(dir_name);
|
||||
|
||||
if profile_path.exists() {
|
||||
let mut size = 0;
|
||||
// 扫描配置的缓存目录
|
||||
for sub in BROWSER_CACHE_DIRS {
|
||||
let target = profile_path.join(sub);
|
||||
if target.exists() {
|
||||
size += get_dir_size_simple(&target);
|
||||
}
|
||||
}
|
||||
|
||||
total_bytes += size;
|
||||
profiles.push(BrowserProfile {
|
||||
name: profile_display_name.to_string(),
|
||||
path_name: dir_name.clone(),
|
||||
cache_size: size,
|
||||
cache_size_str: format_size(size),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(BrowserScanResult {
|
||||
profiles,
|
||||
total_size: format_size(total_bytes),
|
||||
})
|
||||
}
|
||||
|
||||
fn get_dir_size_simple(path: &std::path::Path) -> u64 {
|
||||
walkdir::WalkDir::new(path)
|
||||
.into_iter()
|
||||
.filter_map(|e| e.ok())
|
||||
.filter(|e| e.file_type().is_file())
|
||||
.map(|e| e.metadata().map(|m| m.len()).unwrap_or(0))
|
||||
.sum()
|
||||
}
|
||||
|
||||
pub async fn run_browser_clean(browser: BrowserType, profile_paths: Vec<String>) -> Result<CleanResult, String> {
|
||||
let user_data_path = browser.get_user_data_path()?;
|
||||
let mut total_freed = 0;
|
||||
let mut success_count = 0;
|
||||
let mut fail_count = 0;
|
||||
|
||||
for profile_dir in profile_paths {
|
||||
let profile_path = user_data_path.join(&profile_dir);
|
||||
if profile_path.exists() {
|
||||
// 清理配置的缓存目录
|
||||
for sub in BROWSER_CACHE_DIRS {
|
||||
let target = profile_path.join(sub);
|
||||
if target.exists() {
|
||||
let (f, s, fl) = clean_directory_contents(&target, None);
|
||||
total_freed += f;
|
||||
success_count += s;
|
||||
fail_count += fl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(CleanResult {
|
||||
total_freed: format_size(total_freed),
|
||||
success_count,
|
||||
fail_count,
|
||||
})
|
||||
}
|
||||
|
||||
// --- 内存清理逻辑 ---
|
||||
|
||||
use sysinfo::{System, ProcessesToUpdate};
|
||||
|
||||
#[derive(Serialize, Clone)]
|
||||
pub struct MemoryStats {
|
||||
pub total: u64,
|
||||
pub used: u64,
|
||||
pub free: u64,
|
||||
pub percent: f32,
|
||||
}
|
||||
|
||||
/// 获取当前系统内存状态
|
||||
pub fn get_memory_stats() -> MemoryStats {
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_memory();
|
||||
|
||||
let total = sys.total_memory();
|
||||
let used = sys.used_memory();
|
||||
let free = total.saturating_sub(used);
|
||||
let percent = (used as f32 / total as f32) * 100.0;
|
||||
|
||||
MemoryStats { total, used, free, percent }
|
||||
}
|
||||
|
||||
/// 执行内存压缩 (Empty Working Set)
|
||||
pub async fn run_memory_clean() -> Result<u64, String> {
|
||||
use windows_sys::Win32::System::ProcessStatus::EmptyWorkingSet;
|
||||
use windows_sys::Win32::System::Threading::{OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_SET_QUOTA};
|
||||
use windows_sys::Win32::Foundation::CloseHandle;
|
||||
|
||||
let before = get_memory_stats().used;
|
||||
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_processes(ProcessesToUpdate::All, true);
|
||||
|
||||
for (pid, _) in sys.processes() {
|
||||
let pid_u32 = pid.as_u32();
|
||||
unsafe {
|
||||
let handle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_SET_QUOTA, 0, pid_u32);
|
||||
if handle != std::ptr::null_mut() {
|
||||
EmptyWorkingSet(handle);
|
||||
CloseHandle(handle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 给系统一点点时间反应
|
||||
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
|
||||
|
||||
let after = get_memory_stats().used;
|
||||
let freed = before.saturating_sub(after);
|
||||
Ok(freed)
|
||||
}
|
||||
|
||||
/// 深度内存清理 (Standby List / System Cache)
|
||||
pub async fn run_deep_memory_clean() -> Result<u64, String> {
|
||||
use windows_sys::Win32::System::Memory::SetSystemFileCacheSize;
|
||||
|
||||
let before = get_memory_stats().used;
|
||||
|
||||
unsafe {
|
||||
// -1 (usize::MAX) 表示清空系统文件缓存
|
||||
SetSystemFileCacheSize(usize::MAX, usize::MAX, 0);
|
||||
}
|
||||
|
||||
let after = get_memory_stats().used;
|
||||
let freed = before.saturating_sub(after);
|
||||
Ok(freed)
|
||||
}
|
||||
@@ -1,89 +1,102 @@
|
||||
mod cleaner;
|
||||
use tauri::State;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use tauri::State;
|
||||
|
||||
mod backend;
|
||||
|
||||
#[tauri::command]
|
||||
async fn start_fast_scan() -> cleaner::FastScanResult {
|
||||
cleaner::run_fast_scan().await
|
||||
async fn start_fast_scan() -> backend::models::FastScanResult {
|
||||
backend::fast_clean::run_fast_scan().await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn start_fast_clean(selected_paths: Vec<String>) -> Result<cleaner::CleanResult, String> {
|
||||
cleaner::run_fast_clean(selected_paths).await
|
||||
async fn start_fast_clean(selected_paths: Vec<String>) -> Result<backend::models::CleanResult, String> {
|
||||
backend::fast_clean::run_fast_clean(selected_paths).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn start_full_disk_scan(state: State<'_, cleaner::DiskState>, app_handle: tauri::AppHandle) -> Result<(), String> {
|
||||
cleaner::run_full_scan("C:\\".to_string(), &state, app_handle).await;
|
||||
async fn start_full_disk_scan(
|
||||
state: State<'_, backend::state::DiskState>,
|
||||
app_handle: tauri::AppHandle,
|
||||
) -> Result<(), String> {
|
||||
backend::disk_analysis::run_full_scan("C:\\".to_string(), &state, app_handle).await;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn get_tree_children(path: String, state: State<'_, cleaner::DiskState>) -> Result<Vec<cleaner::FileTreeNode>, String> {
|
||||
Ok(cleaner::get_children(path, &state))
|
||||
async fn get_tree_children(
|
||||
path: String,
|
||||
state: State<'_, backend::state::DiskState>,
|
||||
) -> Result<Vec<backend::models::FileTreeNode>, String> {
|
||||
Ok(backend::disk_analysis::get_children(path, &state))
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn open_in_explorer(path: String) -> Result<(), String> {
|
||||
cleaner::open_explorer(path).await
|
||||
backend::disk_analysis::open_explorer(path).await
|
||||
}
|
||||
|
||||
// --- 高级清理命令 ---
|
||||
|
||||
#[tauri::command]
|
||||
async fn clean_system_components() -> Result<String, String> {
|
||||
cleaner::run_dism_cleanup().await
|
||||
backend::advanced_clean::run_dism_cleanup().await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn clean_thumbnails() -> Result<String, String> {
|
||||
cleaner::clean_thumbnails().await
|
||||
backend::advanced_clean::clean_thumbnails().await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn disable_hibernation() -> Result<String, String> {
|
||||
cleaner::disable_hibernation().await
|
||||
}
|
||||
|
||||
// --- 浏览器清理命令 ---
|
||||
|
||||
#[tauri::command]
|
||||
async fn start_browser_scan(browser: String) -> Result<cleaner::BrowserScanResult, String> {
|
||||
let b_type = if browser == "chrome" { cleaner::BrowserType::Chrome } else { cleaner::BrowserType::Edge };
|
||||
cleaner::run_browser_scan(b_type).await
|
||||
backend::advanced_clean::disable_hibernation().await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn start_browser_clean(browser: String, profiles: Vec<String>) -> Result<cleaner::CleanResult, String> {
|
||||
let b_type = if browser == "chrome" { cleaner::BrowserType::Chrome } else { cleaner::BrowserType::Edge };
|
||||
cleaner::run_browser_clean(b_type, profiles).await
|
||||
async fn start_browser_scan(browser: String) -> Result<backend::models::BrowserScanResult, String> {
|
||||
let browser_type = if browser == "chrome" {
|
||||
backend::models::BrowserType::Chrome
|
||||
} else {
|
||||
backend::models::BrowserType::Edge
|
||||
};
|
||||
|
||||
backend::browser_clean::run_browser_scan(browser_type).await
|
||||
}
|
||||
|
||||
// --- 内存清理命令 ---
|
||||
#[tauri::command]
|
||||
async fn start_browser_clean(
|
||||
browser: String,
|
||||
profiles: Vec<String>,
|
||||
) -> Result<backend::models::CleanResult, String> {
|
||||
let browser_type = if browser == "chrome" {
|
||||
backend::models::BrowserType::Chrome
|
||||
} else {
|
||||
backend::models::BrowserType::Edge
|
||||
};
|
||||
|
||||
backend::browser_clean::run_browser_clean(browser_type, profiles).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn get_memory_stats() -> cleaner::MemoryStats {
|
||||
cleaner::get_memory_stats()
|
||||
async fn get_memory_stats() -> backend::models::MemoryStats {
|
||||
backend::memory_clean::get_memory_stats()
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn run_memory_clean() -> Result<u64, String> {
|
||||
cleaner::run_memory_clean().await
|
||||
backend::memory_clean::run_memory_clean().await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn run_deep_memory_clean() -> Result<u64, String> {
|
||||
cleaner::run_deep_memory_clean().await
|
||||
backend::memory_clean::run_deep_memory_clean().await
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
tauri::Builder::default()
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.manage(cleaner::DiskState {
|
||||
.manage(backend::state::DiskState {
|
||||
dir_sizes: Mutex::new(HashMap::new()),
|
||||
// file_info: Mutex::new(HashMap::new()),
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
start_fast_scan,
|
||||
|
||||
Reference in New Issue
Block a user