Skip to main content

purger_core/
lib.rs

1use serde::{Deserialize, Serialize};
2use std::path::PathBuf;
3
4pub mod cleaner;
5pub mod filter;
6pub mod project;
7pub mod scanner;
8
9pub use cleaner::{CleanPhase, CleanProgress, CleanStrategy, DirectDeleteBackend, ProjectCleaner};
10pub use filter::ProjectFilter;
11pub use project::RustProject;
12pub use scanner::ProjectScanner;
13
14/// 清理结果统计
15#[derive(Debug, Clone, Serialize, Deserialize)]
16pub struct CleanFailure {
17    pub project_name: String,
18    pub project_path: PathBuf,
19    pub error: String,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize)]
23pub struct CleanResult {
24    pub cleaned_projects: usize,
25    pub total_size_freed: u64,
26    pub failed_projects: Vec<String>,
27    #[serde(default)]
28    pub failures: Vec<CleanFailure>,
29    pub duration_ms: u64,
30}
31
32impl Default for CleanResult {
33    fn default() -> Self {
34        Self::new()
35    }
36}
37
38impl CleanResult {
39    pub fn new() -> Self {
40        Self {
41            cleaned_projects: 0,
42            total_size_freed: 0,
43            failed_projects: Vec::new(),
44            failures: Vec::new(),
45            duration_ms: 0,
46        }
47    }
48
49    pub fn add_success(&mut self, size_freed: u64) {
50        self.cleaned_projects += 1;
51        self.total_size_freed += size_freed;
52    }
53
54    pub fn add_failure(&mut self, project_path: String) {
55        self.failed_projects.push(project_path);
56    }
57
58    pub fn add_failure_detail(&mut self, failure: CleanFailure) {
59        self.failed_projects
60            .push(failure.project_path.to_string_lossy().to_string());
61        self.failures.push(failure);
62    }
63
64    pub fn format_size(&self) -> String {
65        format_bytes(self.total_size_freed)
66    }
67}
68
69/// 格式化字节大小为人类可读格式
70pub fn format_bytes(bytes: u64) -> String {
71    const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
72    let mut size = bytes as f64;
73    let mut unit_index = 0;
74
75    while size >= 1024.0 && unit_index < UNITS.len() - 1 {
76        size /= 1024.0;
77        unit_index += 1;
78    }
79
80    if unit_index == 0 {
81        format!("{} {}", bytes, UNITS[unit_index])
82    } else {
83        format!("{:.2} {}", size, UNITS[unit_index])
84    }
85}
86
87#[cfg(test)]
88mod tests {
89    use super::*;
90
91    #[test]
92    fn test_format_bytes() {
93        assert_eq!(format_bytes(0), "0 B");
94        assert_eq!(format_bytes(1023), "1023 B");
95        assert_eq!(format_bytes(1024), "1.00 KB");
96        assert_eq!(format_bytes(1536), "1.50 KB");
97        assert_eq!(format_bytes(1048576), "1.00 MB");
98        assert_eq!(format_bytes(1073741824), "1.00 GB");
99    }
100
101    #[test]
102    fn test_clean_result() {
103        let mut result = CleanResult::new();
104        assert_eq!(result.cleaned_projects, 0);
105        assert_eq!(result.total_size_freed, 0);
106
107        result.add_success(1024);
108        assert_eq!(result.cleaned_projects, 1);
109        assert_eq!(result.total_size_freed, 1024);
110
111        result.add_failure("test_project".to_string());
112        assert_eq!(result.failed_projects.len(), 1);
113    }
114}