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 remove_dir_all::remove_dir_all;
12use std::mem;
13use std::path::{self, Path, PathBuf};
14use std::{fmt, fs, io};
15
16use crate::error::IoResultExt;
17use 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
64pub 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
113pub 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
195pub struct TempDir {
196    path: Box<Path>,
197}
198
199impl 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
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    /// ```
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    /// ```
295    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    /// ```
326    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    /// ```
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
389impl AsRef<Path> for TempDir {
390    fn as_ref(&self) -> &Path {
391        self.path()
392    }
393}
394
395impl fmt::Debug for TempDir {
396    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397        f.debug_struct("TempDir")
398            .field("path", &self.path())
399            .finish()
400    }
401}
402
403impl Drop for TempDir {
404    fn drop(&mut self) {
405        let _ = remove_dir_all(self.path());
406    }
407}
408
409pub(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}