workspacer_cleanup/
cleanup.rs1crate::ix!();
3
4#[async_trait]
5pub trait CleanupWorkspace {
6
7 async fn cleanup_workspace(&self) -> Result<(), WorkspaceError>;
8}
9
10#[async_trait]
11impl<P,H:CrateHandleInterface<P>> CleanupWorkspace for Workspace<P,H>
12where for<'async_trait> P: From<PathBuf> + AsRef<Path> + Send + Sync + 'async_trait
13{
14 async fn cleanup_workspace(&self) -> Result<(), WorkspaceError> {
16
17 let dirs_to_clean = vec![self.as_ref().join("target")];
19 let files_to_clean = vec![self.as_ref().join("Cargo.lock")];
20
21 for dir in dirs_to_clean {
23 if fs::metadata(&dir).await.is_ok() {
24 fs::remove_dir_all(&dir).await.map_err(|_| WorkspaceError::DirectoryRemovalError)?;
25 }
26 }
27
28 for file in files_to_clean {
30 if fs::metadata(&file).await.is_ok() {
31 fs::remove_file(&file).await.map_err(|_| WorkspaceError::FileRemovalError)?;
32 }
33 }
34
35 Ok(())
36 }
37}
38
39#[cfg(test)]
40mod test_cleanup_workspace {
41 use super::*;
42 use std::path::{Path, PathBuf};
43 use tempfile::tempdir;
44 use tokio::fs;
45 use workspacer_3p::async_trait;
46 #[derive(Debug)]
54 struct MockWorkspace {
55 root_dir: PathBuf,
56 }
57
58 impl MockWorkspace {
59 fn new(path: PathBuf) -> Self {
60 Self { root_dir: path }
61 }
62 }
63
64 impl AsRef<Path> for MockWorkspace {
65 fn as_ref(&self) -> &Path {
66 &self.root_dir
67 }
68 }
69
70 #[async_trait]
73 impl CleanupWorkspace for MockWorkspace {
74 async fn cleanup_workspace(&self) -> Result<(), WorkspaceError> {
75 let dirs_to_clean = vec![self.as_ref().join("target")];
80 let files_to_clean = vec![self.as_ref().join("Cargo.lock")];
81
82 for dir in dirs_to_clean {
84 if fs::metadata(&dir).await.is_ok() {
85 fs::remove_dir_all(&dir)
86 .await
87 .map_err(|_| WorkspaceError::DirectoryRemovalError)?;
88 }
89 }
90
91 for file in files_to_clean {
93 if fs::metadata(&file).await.is_ok() {
94 fs::remove_file(&file)
95 .await
96 .map_err(|_| WorkspaceError::FileRemovalError)?;
97 }
98 }
99
100 Ok(())
101 }
102 }
103
104 #[tokio::test]
110 async fn test_cleanup_with_no_target_or_cargo_lock() {
111 let tmp_dir = tempdir().expect("Failed to create temp dir");
112 let workspace = MockWorkspace::new(tmp_dir.path().to_path_buf());
113
114 let result = workspace.cleanup_workspace().await;
117 assert!(result.is_ok(), "Cleanup should succeed if nothing to remove");
118 }
119
120 #[tokio::test]
122 async fn test_cleanup_removes_target_directory() {
123 let tmp_dir = tempdir().expect("Failed to create temp dir");
124 let workspace = MockWorkspace::new(tmp_dir.path().to_path_buf());
125
126 let target_dir = workspace.as_ref().join("target");
128 fs::create_dir_all(&target_dir).await.expect("Failed to create target dir");
129 let sub_file = target_dir.join("some_file.txt");
131 fs::write(&sub_file, b"dummy").await.expect("Failed to write sub file");
132
133 let meta = fs::metadata(&target_dir).await;
135 assert!(meta.is_ok(), "target/ directory should exist before cleanup");
136
137 workspace.cleanup_workspace().await.expect("Cleanup should succeed");
139
140 let meta_after = fs::metadata(&target_dir).await;
142 assert!(meta_after.is_err(), "target/ should be removed by cleanup");
143 }
144
145 #[tokio::test]
147 async fn test_cleanup_removes_cargo_lock_file() {
148 let tmp_dir = tempdir().expect("Failed to create temp dir");
149 let workspace = MockWorkspace::new(tmp_dir.path().to_path_buf());
150
151 let lock_path = workspace.as_ref().join("Cargo.lock");
153 fs::write(&lock_path, b"dummy lock content").await.expect("Failed to write Cargo.lock");
154
155 let meta = fs::metadata(&lock_path).await;
157 assert!(meta.is_ok(), "Cargo.lock should exist");
158
159 workspace.cleanup_workspace().await.expect("Cleanup should succeed");
161
162 let meta_after = fs::metadata(&lock_path).await;
164 assert!(meta_after.is_err(), "Cargo.lock should be removed");
165 }
166
167 #[tokio::test]
169 async fn test_cleanup_removes_both_target_and_cargo_lock() {
170 let tmp_dir = tempdir().expect("Failed to create temp dir");
171 let workspace = MockWorkspace::new(tmp_dir.path().to_path_buf());
172
173 let target_dir = workspace.as_ref().join("target");
175 fs::create_dir_all(&target_dir).await.expect("create target dir");
176 let lock_path = workspace.as_ref().join("Cargo.lock");
178 fs::write(&lock_path, b"dummy").await.expect("write cargo.lock");
179
180 workspace.cleanup_workspace().await.expect("cleanup ok");
182
183 let target_meta = fs::metadata(&target_dir).await;
185 let lock_meta = fs::metadata(&lock_path).await;
186 assert!(target_meta.is_err(), "target/ removed");
187 assert!(lock_meta.is_err(), "Cargo.lock removed");
188 }
189
190 #[cfg(unix)]
191 #[tokio::test]
192 async fn test_cleanup_failure_on_unix() {
193 use std::os::unix::fs::PermissionsExt;
194
195 let tmp_dir = tempdir().expect("create tempdir");
196 let workspace = MockWorkspace::new(tmp_dir.path().to_path_buf());
197
198 let lock_path = workspace.as_ref().join("Cargo.lock");
200 fs::write(&lock_path, b"some content").await.unwrap();
201
202 let mut perms = std::fs::metadata(tmp_dir.path()).unwrap().permissions();
204 perms.set_mode(0o500);
207 std::fs::set_permissions(tmp_dir.path(), perms).expect("set perms on directory");
208
209 let result = workspace.cleanup_workspace().await;
211 assert!(result.is_err(), "Should fail if we can't remove Cargo.lock");
212 match result {
213 Err(WorkspaceError::FileRemovalError) => {
214 }
216 other => panic!("Expected FileRemovalError, got {:?}", other),
217 }
218 }
219
220 #[tokio::test]
222 async fn test_cleanup_idempotent() {
223 let tmp_dir = tempdir().expect("Failed to create temp dir");
224 let workspace = MockWorkspace::new(tmp_dir.path().to_path_buf());
225
226 fs::create_dir_all(workspace.as_ref().join("target")).await.unwrap();
228 fs::write(workspace.as_ref().join("Cargo.lock"), b"xyz").await.unwrap();
229
230 workspace.cleanup_workspace().await.unwrap();
232 workspace.cleanup_workspace().await.unwrap();
236 }
237}