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#[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
69pub 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}