tempfile/dir/
mod.rs

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