1mod platform;
7mod record;
8
9pub use platform::*;
10pub use record::*;
11
12use crate::core::{Artifact, CleanResult};
13use crate::error::{DevSweepError, Result};
14use std::path::Path;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
18pub enum DeleteMethod {
19 #[default]
21 Trash,
22 Permanent,
24 DryRun,
26}
27
28impl DeleteMethod {
29 pub fn from_str(s: &str) -> Option<Self> {
31 match s.to_lowercase().as_str() {
32 "trash" => Some(Self::Trash),
33 "permanent" | "delete" | "rm" => Some(Self::Permanent),
34 "dry-run" | "dryrun" | "dry_run" => Some(Self::DryRun),
35 _ => None,
36 }
37 }
38}
39
40pub fn delete_path(path: &Path, method: DeleteMethod) -> Result<u64> {
42 if !path.exists() {
43 return Ok(0);
44 }
45
46 match method {
47 DeleteMethod::DryRun => {
48 let size = calculate_size(path)?;
50 Ok(size)
51 }
52 DeleteMethod::Trash => {
53 let size = calculate_size(path)?;
54 trash::delete(path).map_err(|e| {
55 DevSweepError::Trash(format!("Failed to move to trash: {}", e))
56 })?;
57 Ok(size)
58 }
59 DeleteMethod::Permanent => {
60 let size = calculate_size(path)?;
61 if path.is_dir() {
62 std::fs::remove_dir_all(path)?;
63 } else {
64 std::fs::remove_file(path)?;
65 }
66 Ok(size)
67 }
68 }
69}
70
71pub fn delete_artifact(artifact: &Artifact, method: DeleteMethod) -> CleanResult {
73 match delete_path(&artifact.path, method) {
74 Ok(_bytes) => CleanResult::success(artifact.clone(), method == DeleteMethod::Trash),
75 Err(e) => CleanResult::failure(artifact.clone(), e.to_string()),
76 }
77}
78
79fn calculate_size(path: &Path) -> Result<u64> {
81 if path.is_file() {
82 return Ok(path.metadata()?.len());
83 }
84
85 let mut size = 0u64;
86 for entry in walkdir::WalkDir::new(path) {
87 if let Ok(entry) = entry {
88 if entry.file_type().is_file() {
89 if let Ok(meta) = entry.metadata() {
90 size += meta.len();
91 }
92 }
93 }
94 }
95 Ok(size)
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101 use tempfile::TempDir;
102
103 #[test]
104 fn test_delete_method_from_str() {
105 assert_eq!(DeleteMethod::from_str("trash"), Some(DeleteMethod::Trash));
106 assert_eq!(DeleteMethod::from_str("permanent"), Some(DeleteMethod::Permanent));
107 assert_eq!(DeleteMethod::from_str("dry-run"), Some(DeleteMethod::DryRun));
108 assert_eq!(DeleteMethod::from_str("invalid"), None);
109 }
110
111 #[test]
112 fn test_dry_run_doesnt_delete() {
113 let temp = TempDir::new().unwrap();
114 let file = temp.path().join("test.txt");
115 std::fs::write(&file, "hello world").unwrap();
116
117 let size = delete_path(&file, DeleteMethod::DryRun).unwrap();
118 assert!(size > 0);
119 assert!(file.exists()); }
121
122 #[test]
123 fn test_permanent_delete() {
124 let temp = TempDir::new().unwrap();
125 let file = temp.path().join("test.txt");
126 std::fs::write(&file, "hello world").unwrap();
127
128 let size = delete_path(&file, DeleteMethod::Permanent).unwrap();
129 assert!(size > 0);
130 assert!(!file.exists()); }
132
133 #[test]
134 fn test_delete_nonexistent() {
135 let path = Path::new("/nonexistent/path/that/doesnt/exist");
136 let size = delete_path(path, DeleteMethod::DryRun).unwrap();
137 assert_eq!(size, 0);
138 }
139
140 #[test]
141 fn test_delete_directory() {
142 let temp = TempDir::new().unwrap();
143 let dir = temp.path().join("subdir");
144 std::fs::create_dir(&dir).unwrap();
145 std::fs::write(dir.join("file1.txt"), "content1").unwrap();
146 std::fs::write(dir.join("file2.txt"), "content2").unwrap();
147
148 let size = delete_path(&dir, DeleteMethod::Permanent).unwrap();
149 assert!(size > 0);
150 assert!(!dir.exists());
151 }
152
153 #[test]
154 fn test_calculate_size() {
155 let temp = TempDir::new().unwrap();
156 let file = temp.path().join("test.txt");
157 std::fs::write(&file, "0123456789").unwrap(); let size = calculate_size(&file).unwrap();
160 assert_eq!(size, 10);
161 }
162}