1use std::path::{Path, PathBuf};
8
9#[derive(Debug)]
13pub struct Scratch {
14 inner: tempfile::TempDir,
15}
16
17impl Scratch {
18 #[must_use]
21 pub fn from_tempdir(inner: tempfile::TempDir) -> Self {
22 Self { inner }
23 }
24
25 #[must_use]
27 pub fn path(&self) -> &Path {
28 self.inner.path()
29 }
30
31 #[must_use]
33 pub fn into_path(self) -> PathBuf {
34 self.inner.keep()
35 }
36
37 pub fn close(self) -> std::io::Result<()> {
43 self.inner.close()
44 }
45}
46
47impl AsRef<Path> for Scratch {
48 fn as_ref(&self) -> &Path {
49 self.inner.path()
50 }
51}
52
53#[derive(Debug)]
56pub struct ScratchFile {
57 inner: tempfile::NamedTempFile,
58}
59
60impl ScratchFile {
61 #[must_use]
64 pub fn from_named(inner: tempfile::NamedTempFile) -> Self {
65 Self { inner }
66 }
67
68 #[must_use]
70 pub fn path(&self) -> &Path {
71 self.inner.path()
72 }
73
74 #[must_use]
76 pub fn as_file(&self) -> &std::fs::File {
77 self.inner.as_file()
78 }
79
80 pub fn as_file_mut(&mut self) -> &mut std::fs::File {
82 self.inner.as_file_mut()
83 }
84
85 pub fn reopen(&self) -> std::io::Result<std::fs::File> {
91 self.inner.reopen()
92 }
93
94 pub fn persist(self, dest: impl AsRef<Path>) -> Result<std::fs::File, tempfile::PersistError> {
100 self.inner.persist(dest)
101 }
102
103 #[must_use]
106 pub fn into_temp_path(self) -> tempfile::TempPath {
107 self.inner.into_temp_path()
108 }
109}
110
111impl AsRef<Path> for ScratchFile {
112 fn as_ref(&self) -> &Path {
113 self.inner.path()
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn scratch_path_under_parent_and_drop_removes_dir() {
123 let parent = tempfile::tempdir().expect("parent tempdir");
124 let inner = tempfile::Builder::new()
125 .prefix("scratch-test-")
126 .tempdir_in(parent.path())
127 .expect("inner tempdir");
128 let inner_path = inner.path().to_path_buf();
129 assert!(inner_path.starts_with(parent.path()));
130 let s = Scratch::from_tempdir(inner);
131 assert!(s.path().is_dir());
132 let kept = s.path().to_path_buf();
133 drop(s);
134 assert!(!kept.exists(), "Scratch Drop must remove the directory");
135 }
136
137 #[test]
138 fn scratch_file_path_under_parent_and_drop_removes_file() {
139 let parent = tempfile::tempdir().expect("parent tempdir");
140 let nf = tempfile::Builder::new()
141 .prefix("scratch-file-test-")
142 .tempfile_in(parent.path())
143 .expect("inner tempfile");
144 let nf_path = nf.path().to_path_buf();
145 assert!(nf_path.starts_with(parent.path()));
146 let f = ScratchFile::from_named(nf);
147 assert!(f.path().is_file());
148 let kept = f.path().to_path_buf();
149 drop(f);
150 assert!(!kept.exists(), "ScratchFile Drop must remove the file");
151 }
152
153 #[test]
154 fn scratch_into_path_keeps_dir() {
155 let parent = tempfile::tempdir().expect("parent tempdir");
156 let inner = tempfile::Builder::new()
157 .prefix("scratch-keep-")
158 .tempdir_in(parent.path())
159 .expect("inner tempdir");
160 let s = Scratch::from_tempdir(inner);
161 let kept = s.into_path();
162 assert!(kept.is_dir(), "into_path() must leave the dir on disk");
163 std::fs::remove_dir_all(&kept).ok();
164 }
165}