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