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