1 // Copyright 2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use remove_dir_all::remove_dir_all;
12 use std::mem;
13 use std::path::{self, Path, PathBuf};
14 use std::{fmt, fs, io};
15
16 use crate::error::IoResultExt;
17 use crate::Builder;
18
19 /// Create a new temporary directory.
20 ///
21 /// The `tempdir` function creates a directory in the file system
22 /// and returns a [`TempDir`].
23 /// The directory will be automatically deleted when the `TempDir`s
24 /// destructor is run.
25 ///
26 /// # Resource Leaking
27 ///
28 /// See [the resource leaking][resource-leaking] docs on `TempDir`.
29 ///
30 /// # Errors
31 ///
32 /// If the directory can not be created, `Err` is returned.
33 ///
34 /// # Examples
35 ///
36 /// ```
37 /// use tempfile::tempdir;
38 /// use std::fs::File;
39 /// use std::io::{self, Write};
40 ///
41 /// # fn main() {
42 /// # if let Err(_) = run() {
43 /// # ::std::process::exit(1);
44 /// # }
45 /// # }
46 /// # fn run() -> Result<(), io::Error> {
47 /// // Create a directory inside of `std::env::temp_dir()`
48 /// let dir = tempdir()?;
49 ///
50 /// let file_path = dir.path().join("my-temporary-note.txt");
51 /// let mut file = File::create(file_path)?;
52 /// writeln!(file, "Brian was here. Briefly.")?;
53 ///
54 /// // `tmp_dir` goes out of scope, the directory as well as
55 /// // `tmp_file` will be deleted here.
56 /// drop(file);
57 /// dir.close()?;
58 /// # Ok(())
59 /// # }
60 /// ```
61 ///
62 /// [`TempDir`]: struct.TempDir.html
63 /// [resource-leaking]: struct.TempDir.html#resource-leaking
tempdir() -> io::Result<TempDir>64 pub fn tempdir() -> io::Result<TempDir> {
65 TempDir::new()
66 }
67
68 /// Create a new temporary directory.
69 ///
70 /// The `tempdir` function creates a directory in the file system
71 /// and returns a [`TempDir`].
72 /// The directory will be automatically deleted when the `TempDir`s
73 /// destructor is run.
74 ///
75 /// # Resource Leaking
76 ///
77 /// See [the resource leaking][resource-leaking] docs on `TempDir`.
78 ///
79 /// # Errors
80 ///
81 /// If the directory can not be created, `Err` is returned.
82 ///
83 /// # Examples
84 ///
85 /// ```
86 /// use tempfile::tempdir;
87 /// use std::fs::File;
88 /// use std::io::{self, Write};
89 ///
90 /// # fn main() {
91 /// # if let Err(_) = run() {
92 /// # ::std::process::exit(1);
93 /// # }
94 /// # }
95 /// # fn run() -> Result<(), io::Error> {
96 /// // Create a directory inside of `std::env::temp_dir()`,
97 /// let dir = tempdir()?;
98 ///
99 /// let file_path = dir.path().join("my-temporary-note.txt");
100 /// let mut file = File::create(file_path)?;
101 /// writeln!(file, "Brian was here. Briefly.")?;
102 ///
103 /// // `tmp_dir` goes out of scope, the directory as well as
104 /// // `tmp_file` will be deleted here.
105 /// drop(file);
106 /// dir.close()?;
107 /// # Ok(())
108 /// # }
109 /// ```
110 ///
111 /// [`TempDir`]: struct.TempDir.html
112 /// [resource-leaking]: struct.TempDir.html#resource-leaking
tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir>113 pub fn tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
114 TempDir::new_in(dir)
115 }
116
117 /// A directory in the filesystem that is automatically deleted when
118 /// it goes out of scope.
119 ///
120 /// The [`TempDir`] type creates a directory on the file system that
121 /// is deleted once it goes out of scope. At construction, the
122 /// `TempDir` creates a new directory with a randomly generated name.
123 ///
124 /// The default constructor, [`TempDir::new()`], creates directories in
125 /// the location returned by [`std::env::temp_dir()`], but `TempDir`
126 /// can be configured to manage a temporary directory in any location
127 /// by constructing with a [`Builder`].
128 ///
129 /// After creating a `TempDir`, work with the file system by doing
130 /// standard [`std::fs`] file system operations on its [`Path`],
131 /// which can be retrieved with [`TempDir::path()`]. Once the `TempDir`
132 /// value is dropped, the directory at the path will be deleted, along
133 /// with any files and directories it contains. It is your responsibility
134 /// to ensure that no further file system operations are attempted
135 /// inside the temporary directory once it has been deleted.
136 ///
137 /// # Resource Leaking
138 ///
139 /// Various platform-specific conditions may cause `TempDir` to fail
140 /// to delete the underlying directory. It's important to ensure that
141 /// handles (like [`File`] and [`ReadDir`]) to files inside the
142 /// directory are dropped before the `TempDir` goes out of scope. The
143 /// `TempDir` destructor will silently ignore any errors in deleting
144 /// the directory; to instead handle errors call [`TempDir::close()`].
145 ///
146 /// Note that if the program exits before the `TempDir` destructor is
147 /// run, such as via [`std::process::exit()`], by segfaulting, or by
148 /// receiving a signal like `SIGINT`, then the temporary directory
149 /// will not be deleted.
150 ///
151 /// # Examples
152 ///
153 /// Create a temporary directory with a generated name:
154 ///
155 /// ```
156 /// use std::fs::File;
157 /// use std::io::Write;
158 /// use tempfile::TempDir;
159 ///
160 /// # use std::io;
161 /// # fn run() -> Result<(), io::Error> {
162 /// // Create a directory inside of `std::env::temp_dir()`
163 /// let tmp_dir = TempDir::new()?;
164 /// # Ok(())
165 /// # }
166 /// ```
167 ///
168 /// Create a temporary directory with a prefix in its name:
169 ///
170 /// ```
171 /// use std::fs::File;
172 /// use std::io::Write;
173 /// use tempfile::Builder;
174 ///
175 /// # use std::io;
176 /// # fn run() -> Result<(), io::Error> {
177 /// // Create a directory inside of `std::env::temp_dir()`,
178 /// // whose name will begin with 'example'.
179 /// let tmp_dir = Builder::new().prefix("example").tempdir()?;
180 /// # Ok(())
181 /// # }
182 /// ```
183 ///
184 /// [`File`]: http://doc.rust-lang.org/std/fs/struct.File.html
185 /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
186 /// [`ReadDir`]: http://doc.rust-lang.org/std/fs/struct.ReadDir.html
187 /// [`Builder`]: struct.Builder.html
188 /// [`TempDir::close()`]: struct.TempDir.html#method.close
189 /// [`TempDir::new()`]: struct.TempDir.html#method.new
190 /// [`TempDir::path()`]: struct.TempDir.html#method.path
191 /// [`TempDir`]: struct.TempDir.html
192 /// [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
193 /// [`std::fs`]: http://doc.rust-lang.org/std/fs/index.html
194 /// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html
195 pub struct TempDir {
196 path: Box<Path>,
197 }
198
199 impl TempDir {
200 /// Attempts to make a temporary directory inside of `env::temp_dir()`.
201 ///
202 /// See [`Builder`] for more configuration.
203 ///
204 /// The directory and everything inside it will be automatically deleted
205 /// once the returned `TempDir` is destroyed.
206 ///
207 /// # Errors
208 ///
209 /// If the directory can not be created, `Err` is returned.
210 ///
211 /// # Examples
212 ///
213 /// ```
214 /// use std::fs::File;
215 /// use std::io::Write;
216 /// use tempfile::TempDir;
217 ///
218 /// # use std::io;
219 /// # fn run() -> Result<(), io::Error> {
220 /// // Create a directory inside of `std::env::temp_dir()`
221 /// let tmp_dir = TempDir::new()?;
222 ///
223 /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
224 /// let mut tmp_file = File::create(file_path)?;
225 /// writeln!(tmp_file, "Brian was here. Briefly.")?;
226 ///
227 /// // `tmp_dir` goes out of scope, the directory as well as
228 /// // `tmp_file` will be deleted here.
229 /// # Ok(())
230 /// # }
231 /// ```
232 ///
233 /// [`Builder`]: struct.Builder.html
new() -> io::Result<TempDir>234 pub fn new() -> io::Result<TempDir> {
235 Builder::new().tempdir()
236 }
237
238 /// Attempts to make a temporary directory inside of `dir`.
239 /// The directory and everything inside it will be automatically
240 /// deleted once the returned `TempDir` is destroyed.
241 ///
242 /// # Errors
243 ///
244 /// If the directory can not be created, `Err` is returned.
245 ///
246 /// # Examples
247 ///
248 /// ```
249 /// use std::fs::{self, File};
250 /// use std::io::Write;
251 /// use tempfile::TempDir;
252 ///
253 /// # use std::io;
254 /// # fn run() -> Result<(), io::Error> {
255 /// // Create a directory inside of the current directory
256 /// let tmp_dir = TempDir::new_in(".")?;
257 /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
258 /// let mut tmp_file = File::create(file_path)?;
259 /// writeln!(tmp_file, "Brian was here. Briefly.")?;
260 /// # Ok(())
261 /// # }
262 /// ```
new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir>263 pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
264 Builder::new().tempdir_in(dir)
265 }
266
267 /// Accesses the [`Path`] to the temporary directory.
268 ///
269 /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
270 ///
271 /// # Examples
272 ///
273 /// ```
274 /// use tempfile::TempDir;
275 ///
276 /// # use std::io;
277 /// # fn run() -> Result<(), io::Error> {
278 /// let tmp_path;
279 ///
280 /// {
281 /// let tmp_dir = TempDir::new()?;
282 /// tmp_path = tmp_dir.path().to_owned();
283 ///
284 /// // Check that the temp directory actually exists.
285 /// assert!(tmp_path.exists());
286 ///
287 /// // End of `tmp_dir` scope, directory will be deleted
288 /// }
289 ///
290 /// // Temp directory should be deleted by now
291 /// assert_eq!(tmp_path.exists(), false);
292 /// # Ok(())
293 /// # }
294 /// ```
path(&self) -> &path::Path295 pub fn path(&self) -> &path::Path {
296 self.path.as_ref()
297 }
298
299 /// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located.
300 ///
301 /// This consumes the [`TempDir`] without deleting directory on the filesystem, meaning that
302 /// the directory will no longer be automatically deleted.
303 ///
304 /// [`TempDir`]: struct.TempDir.html
305 /// [`PathBuf`]: http://doc.rust-lang.org/std/path/struct.PathBuf.html
306 ///
307 /// # Examples
308 ///
309 /// ```
310 /// use std::fs;
311 /// use tempfile::TempDir;
312 ///
313 /// # use std::io;
314 /// # fn run() -> Result<(), io::Error> {
315 /// let tmp_dir = TempDir::new()?;
316 ///
317 /// // Persist the temporary directory to disk,
318 /// // getting the path where it is.
319 /// let tmp_path = tmp_dir.into_path();
320 ///
321 /// // Delete the temporary directory ourselves.
322 /// fs::remove_dir_all(tmp_path)?;
323 /// # Ok(())
324 /// # }
325 /// ```
into_path(self) -> PathBuf326 pub fn into_path(self) -> PathBuf {
327 // Prevent the Drop impl from being called.
328 let mut this = mem::ManuallyDrop::new(self);
329
330 // replace this.path with an empty Box, since an empty Box does not
331 // allocate any heap memory.
332 mem::replace(&mut this.path, PathBuf::new().into_boxed_path()).into()
333 }
334
335 /// Closes and removes the temporary directory, returning a `Result`.
336 ///
337 /// Although `TempDir` removes the directory on drop, in the destructor
338 /// any errors are ignored. To detect errors cleaning up the temporary
339 /// directory, call `close` instead.
340 ///
341 /// # Errors
342 ///
343 /// This function may return a variety of [`std::io::Error`]s that result from deleting
344 /// the files and directories contained with the temporary directory,
345 /// as well as from deleting the temporary directory itself. These errors
346 /// may be platform specific.
347 ///
348 /// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html
349 ///
350 /// # Examples
351 ///
352 /// ```
353 /// use std::fs::File;
354 /// use std::io::Write;
355 /// use tempfile::TempDir;
356 ///
357 /// # use std::io;
358 /// # fn run() -> Result<(), io::Error> {
359 /// // Create a directory inside of `std::env::temp_dir()`.
360 /// let tmp_dir = TempDir::new()?;
361 /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
362 /// let mut tmp_file = File::create(file_path)?;
363 /// writeln!(tmp_file, "Brian was here. Briefly.")?;
364 ///
365 /// // By closing the `TempDir` explicitly we can check that it has
366 /// // been deleted successfully. If we don't close it explicitly,
367 /// // the directory will still be deleted when `tmp_dir` goes out
368 /// // of scope, but we won't know whether deleting the directory
369 /// // succeeded.
370 /// drop(tmp_file);
371 /// tmp_dir.close()?;
372 /// # Ok(())
373 /// # }
374 /// ```
close(mut self) -> io::Result<()>375 pub fn close(mut self) -> io::Result<()> {
376 let result = remove_dir_all(self.path()).with_err_path(|| self.path());
377
378 // Set self.path to empty Box to release the memory, since an empty
379 // Box does not allocate any heap memory.
380 self.path = PathBuf::new().into_boxed_path();
381
382 // Prevent the Drop impl from being called.
383 mem::forget(self);
384
385 result
386 }
387 }
388
389 impl AsRef<Path> for TempDir {
as_ref(&self) -> &Path390 fn as_ref(&self) -> &Path {
391 self.path()
392 }
393 }
394
395 impl fmt::Debug for TempDir {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result396 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397 f.debug_struct("TempDir")
398 .field("path", &self.path())
399 .finish()
400 }
401 }
402
403 impl Drop for TempDir {
drop(&mut self)404 fn drop(&mut self) {
405 let _ = remove_dir_all(self.path());
406 }
407 }
408
create(path: PathBuf) -> io::Result<TempDir>409 pub(crate) fn create(path: PathBuf) -> io::Result<TempDir> {
410 fs::create_dir(&path)
411 .with_err_path(|| &path)
412 .map(|_| TempDir {
413 path: path.into_boxed_path(),
414 })
415 }
416