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