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.
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.
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 keep: 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 /// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located.
386 ///
387 /// This consumes the [`TempDir`] without deleting directory on the filesystem, meaning that
388 /// the directory will no longer be automatically deleted.
389 ///
390 /// [`TempDir`]: struct.TempDir.html
391 /// [`PathBuf`]: http://doc.rust-lang.org/std/path/struct.PathBuf.html
392 ///
393 /// # Examples
394 ///
395 /// ```
396 /// use std::fs;
397 /// use tempfile::TempDir;
398 ///
399 /// let tmp_dir = TempDir::new()?;
400 ///
401 /// // Persist the temporary directory to disk,
402 /// // getting the path where it is.
403 /// let tmp_path = tmp_dir.into_path();
404 ///
405 /// // Delete the temporary directory ourselves.
406 /// fs::remove_dir_all(tmp_path)?;
407 /// # Ok::<(), std::io::Error>(())
408 /// ```
409 #[must_use]
410 pub fn into_path(self) -> PathBuf {
411 // Prevent the Drop impl from being called.
412 let mut this = mem::ManuallyDrop::new(self);
413
414 // replace this.path with an empty Box, since an empty Box does not
415 // allocate any heap memory.
416 mem::replace(&mut this.path, PathBuf::new().into_boxed_path()).into()
417 }
418
419 /// Closes and removes the temporary directory, returning a `Result`.
420 ///
421 /// Although `TempDir` removes the directory on drop, in the destructor
422 /// any errors are ignored. To detect errors cleaning up the temporary
423 /// directory, call `close` instead.
424 ///
425 /// # Errors
426 ///
427 /// This function may return a variety of [`std::io::Error`]s that result from deleting
428 /// the files and directories contained with the temporary directory,
429 /// as well as from deleting the temporary directory itself. These errors
430 /// may be platform specific.
431 ///
432 /// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html
433 ///
434 /// # Examples
435 ///
436 /// ```
437 /// use std::fs::File;
438 /// use std::io::Write;
439 /// use tempfile::TempDir;
440 ///
441 /// // Create a directory inside of `env::temp_dir()`.
442 /// let tmp_dir = TempDir::new()?;
443 /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
444 /// let mut tmp_file = File::create(file_path)?;
445 /// writeln!(tmp_file, "Brian was here. Briefly.")?;
446 ///
447 /// // By closing the `TempDir` explicitly we can check that it has
448 /// // been deleted successfully. If we don't close it explicitly,
449 /// // the directory will still be deleted when `tmp_dir` goes out
450 /// // of scope, but we won't know whether deleting the directory
451 /// // succeeded.
452 /// drop(tmp_file);
453 /// tmp_dir.close()?;
454 /// # Ok::<(), std::io::Error>(())
455 /// ```
456 pub fn close(mut self) -> io::Result<()> {
457 let result = remove_dir_all(self.path()).with_err_path(|| self.path());
458
459 // Set self.path to empty Box to release the memory, since an empty
460 // Box does not allocate any heap memory.
461 self.path = PathBuf::new().into_boxed_path();
462
463 // Prevent the Drop impl from being called.
464 mem::forget(self);
465
466 result
467 }
468}
469
470impl AsRef<Path> for TempDir {
471 fn as_ref(&self) -> &Path {
472 self.path()
473 }
474}
475
476impl fmt::Debug for TempDir {
477 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
478 f.debug_struct("TempDir")
479 .field("path", &self.path())
480 .finish()
481 }
482}
483
484impl Drop for TempDir {
485 fn drop(&mut self) {
486 if !self.keep {
487 let _ = remove_dir_all(self.path());
488 }
489 }
490}
491
492pub(crate) fn create(
493 path: PathBuf,
494 permissions: Option<&std::fs::Permissions>,
495 keep: bool,
496) -> io::Result<TempDir> {
497 imp::create(path, permissions, keep)
498}
499
500mod imp;