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