tempfile/dir/
mod.rs

1// Copyright 2015 The Rust Project Developers.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::ffi::OsStr;
10use std::fs::remove_dir_all;
11use std::mem;
12use std::path::{self, Path, PathBuf};
13use std::{fmt, io};
14
15use crate::error::IoResultExt;
16use crate::Builder;
17
18#[cfg(doc)]
19use crate::env;
20
21/// Create a new temporary directory. Also see [`tempdir_in`].
22///
23/// The `tempdir` function creates a directory in the file system and returns a
24/// [`TempDir`]. The directory will be automatically deleted when the `TempDir`'s
25/// destructor is run.
26///
27/// # Resource Leaking
28///
29/// See [the resource leaking][resource-leaking] docs on `TempDir`.
30///
31/// # Security
32///
33/// Temporary directories are created with the default permissions unless otherwise
34/// specified via [`Builder::permissions`]. Depending on your platform, this may make
35/// them world-readable.
36///
37/// # Errors
38///
39/// If the directory can not be created, `Err` is returned.
40///
41/// # Examples
42///
43/// ```
44/// use tempfile::tempdir;
45/// use std::fs::File;
46/// use std::io::Write;
47///
48/// // Create a directory inside of `env::temp_dir()`
49/// let tmp_dir = tempdir()?;
50///
51/// let file_path = tmp_dir.path().join("my-temporary-note.txt");
52/// let mut tmp_file = File::create(file_path)?;
53/// writeln!(tmp_file, "Brian was here. Briefly.")?;
54///
55/// // `tmp_dir` goes out of scope, the directory as well as
56/// // `tmp_file` will be deleted here.
57/// drop(tmp_file);
58/// tmp_dir.close()?;
59/// # Ok::<(), std::io::Error>(())
60/// ```
61///
62/// [`TempDir`]: struct.TempDir.html
63/// [resource-leaking]: struct.TempDir.html#resource-leaking
64pub fn tempdir() -> io::Result<TempDir> {
65    TempDir::new()
66}
67
68/// Create a new temporary directory in a specific directory. Also see [`tempdir`].
69///
70/// The `tempdir_in` function creates a directory in the specified directory
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_in;
87/// use std::fs::File;
88/// use std::io::Write;
89///
90/// // Create a directory inside of the current directory.
91/// let tmp_dir = tempdir_in(".")?;
92///
93/// let file_path = tmp_dir.path().join("my-temporary-note.txt");
94/// let mut tmp_file = File::create(file_path)?;
95/// writeln!(tmp_file, "Brian was here. Briefly.")?;
96///
97/// // `tmp_dir` goes out of scope, the directory as well as
98/// // `tmp_file` will be deleted here.
99/// drop(tmp_file);
100/// tmp_dir.close()?;
101/// # Ok::<(), std::io::Error>(())
102/// ```
103///
104/// [`TempDir`]: struct.TempDir.html
105/// [resource-leaking]: struct.TempDir.html#resource-leaking
106pub fn tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
107    TempDir::new_in(dir)
108}
109
110/// A directory in the filesystem that is automatically deleted when
111/// it goes out of scope.
112///
113/// The [`TempDir`] type creates a directory on the file system that
114/// is deleted once it goes out of scope. At construction, the
115/// `TempDir` creates a new directory with a randomly generated name.
116///
117/// The default constructor, [`TempDir::new()`], creates directories in
118/// the location returned by [`env::temp_dir()`], but `TempDir`
119/// can be configured to manage a temporary directory in any location
120/// by constructing with a [`Builder`].
121///
122/// After creating a `TempDir`, work with the file system by doing
123/// standard [`std::fs`] file system operations on its [`Path`],
124/// which can be retrieved with [`TempDir::path()`]. Once the `TempDir`
125/// value is dropped, the directory at the path will be deleted, along
126/// with any files and directories it contains. It is your responsibility
127/// to ensure that no further file system operations are attempted
128/// inside the temporary directory once it has been deleted.
129///
130/// # Resource Leaking
131///
132/// Various platform-specific conditions may cause `TempDir` to fail
133/// to delete the underlying directory. It's important to ensure that
134/// handles (like [`File`] and [`ReadDir`]) to files inside the
135/// directory are dropped before the `TempDir` goes out of scope. The
136/// `TempDir` destructor will silently ignore any errors in deleting
137/// the directory; to instead handle errors call [`TempDir::close()`].
138///
139/// Note that if the program exits before the `TempDir` destructor is
140/// run, such as via [`std::process::exit()`], by segfaulting, or by
141/// receiving a signal like `SIGINT`, then the temporary directory
142/// will not be deleted.
143///
144/// # Examples
145///
146/// Create a temporary directory with a generated name:
147///
148/// ```
149/// use std::fs::File;
150/// use std::io::Write;
151/// use tempfile::TempDir;
152///
153/// // Create a directory inside of `env::temp_dir()`
154/// let tmp_dir = TempDir::new()?;
155/// # Ok::<(), std::io::Error>(())
156/// ```
157///
158/// Create a temporary directory with a prefix in its name:
159///
160/// ```
161/// use std::fs::File;
162/// use std::io::Write;
163/// use tempfile::Builder;
164///
165/// // Create a directory inside of `env::temp_dir()`,
166/// // whose name will begin with 'example'.
167/// let tmp_dir = Builder::new().prefix("example").tempdir()?;
168/// # Ok::<(), std::io::Error>(())
169/// ```
170///
171/// [`File`]: http://doc.rust-lang.org/std/fs/struct.File.html
172/// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
173/// [`ReadDir`]: http://doc.rust-lang.org/std/fs/struct.ReadDir.html
174/// [`Builder`]: struct.Builder.html
175/// [`TempDir::close()`]: struct.TempDir.html#method.close
176/// [`TempDir::new()`]: struct.TempDir.html#method.new
177/// [`TempDir::path()`]: struct.TempDir.html#method.path
178/// [`TempDir`]: struct.TempDir.html
179/// [`std::fs`]: http://doc.rust-lang.org/std/fs/index.html
180/// [`std::process::exit()`]: http://doc.rust-lang.org/std/process/fn.exit.html
181pub struct TempDir {
182    path: Box<Path>,
183    disable_cleanup: bool,
184}
185
186impl TempDir {
187    /// Attempts to make a temporary directory inside of `env::temp_dir()`.
188    ///
189    /// See [`Builder`] for more configuration.
190    ///
191    /// The directory and everything inside it will be automatically deleted
192    /// once the returned `TempDir` is destroyed.
193    ///
194    /// # Errors
195    ///
196    /// If the directory can not be created, `Err` is returned.
197    ///
198    /// # Examples
199    ///
200    /// ```
201    /// use std::fs::File;
202    /// use std::io::Write;
203    /// use tempfile::TempDir;
204    ///
205    /// // Create a directory inside of `env::temp_dir()`
206    /// let tmp_dir = TempDir::new()?;
207    ///
208    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
209    /// let mut tmp_file = File::create(file_path)?;
210    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
211    ///
212    /// // `tmp_dir` goes out of scope, the directory as well as
213    /// // `tmp_file` will be deleted here.
214    /// # Ok::<(), std::io::Error>(())
215    /// ```
216    ///
217    /// [`Builder`]: struct.Builder.html
218    pub fn new() -> io::Result<TempDir> {
219        Builder::new().tempdir()
220    }
221
222    /// Attempts to make a temporary directory inside of `dir`.
223    /// The directory and everything inside it will be automatically
224    /// deleted once the returned `TempDir` is destroyed.
225    ///
226    /// # Errors
227    ///
228    /// If the directory can not be created, `Err` is returned.
229    ///
230    /// # Examples
231    ///
232    /// ```
233    /// use std::fs::{self, File};
234    /// use std::io::Write;
235    /// use tempfile::TempDir;
236    ///
237    /// // Create a directory inside of the current directory
238    /// let tmp_dir = TempDir::new_in(".")?;
239    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
240    /// let mut tmp_file = File::create(file_path)?;
241    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
242    /// # Ok::<(), std::io::Error>(())
243    /// ```
244    pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir> {
245        Builder::new().tempdir_in(dir)
246    }
247
248    /// Attempts to make a temporary directory with the specified prefix inside of
249    /// `env::temp_dir()`. The directory and everything inside it will be automatically
250    /// deleted once the returned `TempDir` is destroyed.
251    ///
252    /// # Errors
253    ///
254    /// If the directory can not be created, `Err` is returned.
255    ///
256    /// # Examples
257    ///
258    /// ```
259    /// use std::fs::{self, File};
260    /// use std::io::Write;
261    /// use tempfile::TempDir;
262    ///
263    /// // Create a directory inside of the current directory
264    /// let tmp_dir = TempDir::with_prefix("foo-")?;
265    /// let tmp_name = tmp_dir.path().file_name().unwrap().to_str().unwrap();
266    /// assert!(tmp_name.starts_with("foo-"));
267    /// # Ok::<(), std::io::Error>(())
268    /// ```
269    pub fn with_prefix<S: AsRef<OsStr>>(prefix: S) -> io::Result<TempDir> {
270        Builder::new().prefix(&prefix).tempdir()
271    }
272
273    /// Attempts to make a temporary directory with the specified suffix inside of
274    /// `env::temp_dir()`. The directory and everything inside it will be automatically
275    /// deleted once the returned `TempDir` is destroyed.
276    ///
277    /// # Errors
278    ///
279    /// If the directory can not be created, `Err` is returned.
280    ///
281    /// # Examples
282    ///
283    /// ```
284    /// use std::fs::{self, File};
285    /// use std::io::Write;
286    /// use tempfile::TempDir;
287    ///
288    /// // Create a directory inside of the current directory
289    /// let tmp_dir = TempDir::with_suffix("-foo")?;
290    /// let tmp_name = tmp_dir.path().file_name().unwrap().to_str().unwrap();
291    /// assert!(tmp_name.ends_with("-foo"));
292    /// # Ok::<(), std::io::Error>(())
293    /// ```
294    pub fn with_suffix<S: AsRef<OsStr>>(suffix: S) -> io::Result<TempDir> {
295        Builder::new().suffix(&suffix).tempdir()
296    }
297    /// Attempts to make a temporary directory with the specified prefix inside
298    /// the specified directory. The directory and everything inside it will be
299    /// automatically deleted once the returned `TempDir` is destroyed.
300    ///
301    /// # Errors
302    ///
303    /// If the directory can not be created, `Err` is returned.
304    ///
305    /// # Examples
306    ///
307    /// ```
308    /// use std::fs::{self, File};
309    /// use std::io::Write;
310    /// use tempfile::TempDir;
311    ///
312    /// // Create a directory inside of the current directory
313    /// let tmp_dir = TempDir::with_suffix_in("-foo", ".")?;
314    /// let tmp_name = tmp_dir.path().file_name().unwrap().to_str().unwrap();
315    /// assert!(tmp_name.ends_with("-foo"));
316    /// # Ok::<(), std::io::Error>(())
317    /// ```
318    pub fn with_suffix_in<S: AsRef<OsStr>, P: AsRef<Path>>(
319        suffix: S,
320        dir: P,
321    ) -> io::Result<TempDir> {
322        Builder::new().suffix(&suffix).tempdir_in(dir)
323    }
324
325    /// Attempts to make a temporary directory with the specified prefix inside
326    /// the specified directory. The directory and everything inside it will be
327    /// automatically deleted once the returned `TempDir` is destroyed.
328    ///
329    /// # Errors
330    ///
331    /// If the directory can not be created, `Err` is returned.
332    ///
333    /// # Examples
334    ///
335    /// ```
336    /// use std::fs::{self, File};
337    /// use std::io::Write;
338    /// use tempfile::TempDir;
339    ///
340    /// // Create a directory inside of the current directory
341    /// let tmp_dir = TempDir::with_prefix_in("foo-", ".")?;
342    /// let tmp_name = tmp_dir.path().file_name().unwrap().to_str().unwrap();
343    /// assert!(tmp_name.starts_with("foo-"));
344    /// # Ok::<(), std::io::Error>(())
345    /// ```
346    pub fn with_prefix_in<S: AsRef<OsStr>, P: AsRef<Path>>(
347        prefix: S,
348        dir: P,
349    ) -> io::Result<TempDir> {
350        Builder::new().prefix(&prefix).tempdir_in(dir)
351    }
352
353    /// Accesses the [`Path`] to the temporary directory.
354    ///
355    /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
356    ///
357    /// # Examples
358    ///
359    /// ```
360    /// use tempfile::TempDir;
361    ///
362    /// let tmp_path;
363    ///
364    /// {
365    ///    let tmp_dir = TempDir::new()?;
366    ///    tmp_path = tmp_dir.path().to_owned();
367    ///
368    ///    // Check that the temp directory actually exists.
369    ///    assert!(tmp_path.exists());
370    ///
371    ///    // End of `tmp_dir` scope, directory will be deleted
372    /// }
373    ///
374    /// // Temp directory should be deleted by now
375    /// assert_eq!(tmp_path.exists(), false);
376    /// # Ok::<(), std::io::Error>(())
377    /// ```
378    #[must_use]
379    pub fn path(&self) -> &path::Path {
380        self.path.as_ref()
381    }
382
383    /// Deprecated alias for [`TempDir::keep`].
384    #[must_use]
385    #[deprecated = "use TempDir::keep()"]
386    pub fn into_path(self) -> PathBuf {
387        self.keep()
388    }
389
390    /// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located.
391    ///
392    /// This consumes the [`TempDir`] without deleting directory on the filesystem, meaning that
393    /// the directory will no longer be automatically deleted.
394    ///
395    /// If you want to disable automatic cleanup of the temporary directory in-place, keeping the
396    /// `TempDir` as-is, use [`TempDir::disable_cleanup`] instead.
397    ///
398    /// [`TempDir`]: struct.TempDir.html
399    /// [`PathBuf`]: http://doc.rust-lang.org/std/path/struct.PathBuf.html
400    ///
401    /// # Examples
402    ///
403    /// ```
404    /// use std::fs;
405    /// use tempfile::TempDir;
406    ///
407    /// let tmp_dir = TempDir::new()?;
408    ///
409    /// // Persist the temporary directory to disk,
410    /// // getting the path where it is.
411    /// let tmp_path = tmp_dir.keep();
412    ///
413    /// // Delete the temporary directory ourselves.
414    /// fs::remove_dir_all(tmp_path)?;
415    /// # Ok::<(), std::io::Error>(())
416    /// ```
417    #[must_use]
418    pub fn keep(mut self) -> PathBuf {
419        self.disable_cleanup(true);
420        mem::replace(&mut self.path, PathBuf::new().into_boxed_path()).into()
421    }
422
423    /// Disable cleanup of the temporary directory. If `disable_cleanup` is `true`, the temporary
424    /// directory will not be deleted when this `TempDir` is dropped. This method is equivalent to
425    /// calling [`Builder::disable_cleanup`] when creating the `TempDir`.
426    ///
427    /// **NOTE:** this method is primarily useful for testing/debugging. If you want to simply turn
428    /// a temporary directory into a non-temporary directory, prefer [`TempDir::keep`].
429    pub fn disable_cleanup(&mut self, disable_cleanup: bool) {
430        self.disable_cleanup = disable_cleanup
431    }
432
433    /// Closes and removes the temporary directory, returning a `Result`.
434    ///
435    /// Although `TempDir` removes the directory on drop, in the destructor
436    /// any errors are ignored. To detect errors cleaning up the temporary
437    /// directory, call `close` instead.
438    ///
439    /// # Errors
440    ///
441    /// This function may return a variety of [`std::io::Error`]s that result from deleting
442    /// the files and directories contained with the temporary directory,
443    /// as well as from deleting the temporary directory itself. These errors
444    /// may be platform specific.
445    ///
446    /// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html
447    ///
448    /// # Examples
449    ///
450    /// ```
451    /// use std::fs::File;
452    /// use std::io::Write;
453    /// use tempfile::TempDir;
454    ///
455    /// // Create a directory inside of `env::temp_dir()`.
456    /// let tmp_dir = TempDir::new()?;
457    /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
458    /// let mut tmp_file = File::create(file_path)?;
459    /// writeln!(tmp_file, "Brian was here. Briefly.")?;
460    ///
461    /// // By closing the `TempDir` explicitly we can check that it has
462    /// // been deleted successfully. If we don't close it explicitly,
463    /// // the directory will still be deleted when `tmp_dir` goes out
464    /// // of scope, but we won't know whether deleting the directory
465    /// // succeeded.
466    /// drop(tmp_file);
467    /// tmp_dir.close()?;
468    /// # Ok::<(), std::io::Error>(())
469    /// ```
470    pub fn close(mut self) -> io::Result<()> {
471        let result = remove_dir_all(self.path()).with_err_path(|| self.path());
472
473        // Set self.path to empty Box to release the memory, since an empty
474        // Box does not allocate any heap memory.
475        self.path = PathBuf::new().into_boxed_path();
476
477        // Prevent the Drop impl from being called.
478        mem::forget(self);
479
480        result
481    }
482}
483
484impl AsRef<Path> for TempDir {
485    fn as_ref(&self) -> &Path {
486        self.path()
487    }
488}
489
490impl fmt::Debug for TempDir {
491    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492        f.debug_struct("TempDir")
493            .field("path", &self.path())
494            .finish()
495    }
496}
497
498impl Drop for TempDir {
499    fn drop(&mut self) {
500        if !self.disable_cleanup {
501            let _ = remove_dir_all(self.path());
502        }
503    }
504}
505
506pub(crate) fn create(
507    path: PathBuf,
508    permissions: Option<&std::fs::Permissions>,
509    disable_cleanup: bool,
510) -> io::Result<TempDir> {
511    imp::create(path, permissions, disable_cleanup)
512}
513
514mod imp;