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