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