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 
11 use std::ffi::OsStr;
12 use std::fs::remove_dir_all;
13 use std::mem;
14 use std::path::{self, Path, PathBuf};
15 use std::{fmt, io};
16 
17 use crate::error::IoResultExt;
18 use crate::Builder;
19 
20 #[cfg(doc)]
21 use 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
tempdir() -> io::Result<TempDir>61 pub 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
tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir>103 pub 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
178 pub struct TempDir {
179     path: Box<Path>,
180     keep: bool,
181 }
182 
183 impl 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
new() -> io::Result<TempDir>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     /// ```
new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir>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     /// ```
with_prefix<S: AsRef<OsStr>>(prefix: S) -> io::Result<TempDir>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 prefix inside
271     /// the specified directory. The directory and everything inside it will be
272     /// automatically 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_prefix_in("foo-", ".")?;
287     /// let tmp_name = tmp_dir.path().file_name().unwrap().to_str().unwrap();
288     /// assert!(tmp_name.starts_with("foo-"));
289     /// # Ok::<(), std::io::Error>(())
290     /// ```
with_prefix_in<S: AsRef<OsStr>, P: AsRef<Path>>( prefix: S, dir: P, ) -> io::Result<TempDir>291     pub fn with_prefix_in<S: AsRef<OsStr>, P: AsRef<Path>>(
292         prefix: S,
293         dir: P,
294     ) -> io::Result<TempDir> {
295         Builder::new().prefix(&prefix).tempdir_in(dir)
296     }
297 
298     /// Accesses the [`Path`] to the temporary directory.
299     ///
300     /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
301     ///
302     /// # Examples
303     ///
304     /// ```
305     /// use tempfile::TempDir;
306     ///
307     /// let tmp_path;
308     ///
309     /// {
310     ///    let tmp_dir = TempDir::new()?;
311     ///    tmp_path = tmp_dir.path().to_owned();
312     ///
313     ///    // Check that the temp directory actually exists.
314     ///    assert!(tmp_path.exists());
315     ///
316     ///    // End of `tmp_dir` scope, directory will be deleted
317     /// }
318     ///
319     /// // Temp directory should be deleted by now
320     /// assert_eq!(tmp_path.exists(), false);
321     /// # Ok::<(), std::io::Error>(())
322     /// ```
323     #[must_use]
path(&self) -> &path::Path324     pub fn path(&self) -> &path::Path {
325         self.path.as_ref()
326     }
327 
328     /// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located.
329     ///
330     /// This consumes the [`TempDir`] without deleting directory on the filesystem, meaning that
331     /// the directory will no longer be automatically deleted.
332     ///
333     /// [`TempDir`]: struct.TempDir.html
334     /// [`PathBuf`]: http://doc.rust-lang.org/std/path/struct.PathBuf.html
335     ///
336     /// # Examples
337     ///
338     /// ```
339     /// use std::fs;
340     /// use tempfile::TempDir;
341     ///
342     /// let tmp_dir = TempDir::new()?;
343     ///
344     /// // Persist the temporary directory to disk,
345     /// // getting the path where it is.
346     /// let tmp_path = tmp_dir.into_path();
347     ///
348     /// // Delete the temporary directory ourselves.
349     /// fs::remove_dir_all(tmp_path)?;
350     /// # Ok::<(), std::io::Error>(())
351     /// ```
352     #[must_use]
into_path(self) -> PathBuf353     pub fn into_path(self) -> PathBuf {
354         // Prevent the Drop impl from being called.
355         let mut this = mem::ManuallyDrop::new(self);
356 
357         // replace this.path with an empty Box, since an empty Box does not
358         // allocate any heap memory.
359         mem::replace(&mut this.path, PathBuf::new().into_boxed_path()).into()
360     }
361 
362     /// Closes and removes the temporary directory, returning a `Result`.
363     ///
364     /// Although `TempDir` removes the directory on drop, in the destructor
365     /// any errors are ignored. To detect errors cleaning up the temporary
366     /// directory, call `close` instead.
367     ///
368     /// # Errors
369     ///
370     /// This function may return a variety of [`std::io::Error`]s that result from deleting
371     /// the files and directories contained with the temporary directory,
372     /// as well as from deleting the temporary directory itself. These errors
373     /// may be platform specific.
374     ///
375     /// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html
376     ///
377     /// # Examples
378     ///
379     /// ```
380     /// use std::fs::File;
381     /// use std::io::Write;
382     /// use tempfile::TempDir;
383     ///
384     /// // Create a directory inside of `env::temp_dir()`.
385     /// let tmp_dir = TempDir::new()?;
386     /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
387     /// let mut tmp_file = File::create(file_path)?;
388     /// writeln!(tmp_file, "Brian was here. Briefly.")?;
389     ///
390     /// // By closing the `TempDir` explicitly we can check that it has
391     /// // been deleted successfully. If we don't close it explicitly,
392     /// // the directory will still be deleted when `tmp_dir` goes out
393     /// // of scope, but we won't know whether deleting the directory
394     /// // succeeded.
395     /// drop(tmp_file);
396     /// tmp_dir.close()?;
397     /// # Ok::<(), std::io::Error>(())
398     /// ```
close(mut self) -> io::Result<()>399     pub fn close(mut self) -> io::Result<()> {
400         let result = remove_dir_all(self.path()).with_err_path(|| self.path());
401 
402         // Set self.path to empty Box to release the memory, since an empty
403         // Box does not allocate any heap memory.
404         self.path = PathBuf::new().into_boxed_path();
405 
406         // Prevent the Drop impl from being called.
407         mem::forget(self);
408 
409         result
410     }
411 }
412 
413 impl AsRef<Path> for TempDir {
as_ref(&self) -> &Path414     fn as_ref(&self) -> &Path {
415         self.path()
416     }
417 }
418 
419 impl fmt::Debug for TempDir {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result420     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421         f.debug_struct("TempDir")
422             .field("path", &self.path())
423             .finish()
424     }
425 }
426 
427 impl Drop for TempDir {
drop(&mut self)428     fn drop(&mut self) {
429         if !self.keep {
430             let _ = remove_dir_all(self.path());
431         }
432     }
433 }
434 
create( path: PathBuf, permissions: Option<&std::fs::Permissions>, keep: bool, ) -> io::Result<TempDir>435 pub(crate) fn create(
436     path: PathBuf,
437     permissions: Option<&std::fs::Permissions>,
438     keep: bool,
439 ) -> io::Result<TempDir> {
440     imp::create(path, permissions, keep)
441 }
442 
443 mod imp;
444