• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 remove_dir_all::remove_dir_all;
12 use std::mem;
13 use std::path::{self, Path, PathBuf};
14 use std::{fmt, fs, io};
15 
16 use crate::error::IoResultExt;
17 use 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
tempdir() -> io::Result<TempDir>64 pub 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
tempdir_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir>113 pub 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
195 pub struct TempDir {
196     path: Box<Path>,
197 }
198 
199 impl 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
new() -> io::Result<TempDir>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     /// ```
new_in<P: AsRef<Path>>(dir: P) -> io::Result<TempDir>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     /// ```
path(&self) -> &path::Path295     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     /// ```
into_path(self) -> PathBuf326     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     /// ```
close(mut self) -> io::Result<()>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 
389 impl AsRef<Path> for TempDir {
as_ref(&self) -> &Path390     fn as_ref(&self) -> &Path {
391         self.path()
392     }
393 }
394 
395 impl fmt::Debug for TempDir {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result396     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
397         f.debug_struct("TempDir")
398             .field("path", &self.path())
399             .finish()
400     }
401 }
402 
403 impl Drop for TempDir {
drop(&mut self)404     fn drop(&mut self) {
405         let _ = remove_dir_all(self.path());
406     }
407 }
408 
create(path: PathBuf) -> io::Result<TempDir>409 pub(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 }
416