aster_forge_utils/
raii.rs1use std::path::{Path, PathBuf};
8
9pub struct TempFileGuard {
14 path: PathBuf,
15 cleanup_label: &'static str,
16}
17
18impl TempFileGuard {
19 pub fn new(path: PathBuf, cleanup_label: &'static str) -> Self {
21 Self {
22 path,
23 cleanup_label,
24 }
25 }
26
27 pub fn path(&self) -> &Path {
29 &self.path
30 }
31}
32
33impl Drop for TempFileGuard {
34 fn drop(&mut self) {
35 if let Err(error) = std::fs::remove_file(&self.path)
36 && error.kind() != std::io::ErrorKind::NotFound
37 {
38 tracing::warn!(
39 path = ?self.path,
40 cleanup = self.cleanup_label,
41 "failed to cleanup temp file: {error}"
42 );
43 }
44 }
45}
46
47pub struct TempDirGuard {
52 path: PathBuf,
53 cleanup_label: &'static str,
54}
55
56impl TempDirGuard {
57 pub fn new(path: PathBuf, cleanup_label: &'static str) -> Self {
59 Self {
60 path,
61 cleanup_label,
62 }
63 }
64
65 pub fn path(&self) -> &Path {
67 &self.path
68 }
69}
70
71impl Drop for TempDirGuard {
72 fn drop(&mut self) {
73 if let Err(error) = std::fs::remove_dir_all(&self.path)
74 && error.kind() != std::io::ErrorKind::NotFound
75 {
76 tracing::warn!(
77 path = %self.path.display(),
78 cleanup = self.cleanup_label,
79 "failed to cleanup temp dir: {error}"
80 );
81 }
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::{TempDirGuard, TempFileGuard};
88 use std::path::PathBuf;
89
90 fn temp_path(name: &str) -> PathBuf {
91 std::env::temp_dir().join(format!("aster-forge-{name}-{}", uuid::Uuid::new_v4()))
92 }
93
94 #[test]
95 fn temp_file_guard_removes_file_on_drop() {
96 let path = temp_path("file-guard");
97 std::fs::write(&path, b"temporary").expect("temp file should be created");
98
99 {
100 let guard = TempFileGuard::new(path.clone(), "test-temp-file");
101 assert_eq!(guard.path(), path.as_path());
102 assert!(path.exists());
103 }
104
105 assert!(!path.exists());
106 }
107
108 #[test]
109 fn temp_file_guard_ignores_missing_file() {
110 let path = temp_path("missing-file-guard");
111 {
112 let guard = TempFileGuard::new(path.clone(), "test-missing-temp-file");
113 assert_eq!(guard.path(), path.as_path());
114 }
115
116 assert!(!path.exists());
117 }
118
119 #[test]
120 fn temp_dir_guard_removes_directory_tree_on_drop() {
121 let path = temp_path("dir-guard");
122 let nested = path.join("nested");
123 std::fs::create_dir_all(&nested).expect("nested temp dir should be created");
124 std::fs::write(nested.join("file.txt"), b"temporary")
125 .expect("nested temp file should be created");
126
127 {
128 let guard = TempDirGuard::new(path.clone(), "test-temp-dir");
129 assert_eq!(guard.path(), path.as_path());
130 assert!(nested.exists());
131 }
132
133 assert!(!path.exists());
134 }
135
136 #[test]
137 fn temp_dir_guard_ignores_missing_directory() {
138 let path = temp_path("missing-dir-guard");
139 {
140 let guard = TempDirGuard::new(path.clone(), "test-missing-temp-dir");
141 assert_eq!(guard.path(), path.as_path());
142 }
143
144 assert!(!path.exists());
145 }
146}