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
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
61pub 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
103pub 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
178pub struct TempDir {
179 path: Box<Path>,
180 keep: bool,
181}
182
183impl 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
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 /// ```
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 /// ```
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 suffix inside of
271 /// `env::temp_dir()`. The directory and everything inside it will be automatically
272 /// 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_suffix("-foo")?;
287 /// let tmp_name = tmp_dir.path().file_name().unwrap().to_str().unwrap();
288 /// assert!(tmp_name.ends_with("-foo"));
289 /// # Ok::<(), std::io::Error>(())
290 /// ```
291 pub fn with_suffix<S: AsRef<OsStr>>(suffix: S) -> io::Result<TempDir> {
292 Builder::new().suffix(&suffix).tempdir()
293 }
294 /// Attempts to make a temporary directory with the specified prefix inside
295 /// the specified directory. The directory and everything inside it will be
296 /// automatically deleted once the returned `TempDir` is destroyed.
297 ///
298 /// # Errors
299 ///
300 /// If the directory can not be created, `Err` is returned.
301 ///
302 /// # Examples
303 ///
304 /// ```
305 /// use std::fs::{self, File};
306 /// use std::io::Write;
307 /// use tempfile::TempDir;
308 ///
309 /// // Create a directory inside of the current directory
310 /// let tmp_dir = TempDir::with_suffix_in("-foo", ".")?;
311 /// let tmp_name = tmp_dir.path().file_name().unwrap().to_str().unwrap();
312 /// assert!(tmp_name.ends_with("-foo"));
313 /// # Ok::<(), std::io::Error>(())
314 /// ```
315 pub fn with_suffix_in<S: AsRef<OsStr>, P: AsRef<Path>>(
316 suffix: S,
317 dir: P,
318 ) -> io::Result<TempDir> {
319 Builder::new().suffix(&suffix).tempdir_in(dir)
320 }
321
322 /// Attempts to make a temporary directory with the specified prefix inside
323 /// the specified directory. The directory and everything inside it will be
324 /// automatically deleted once the returned `TempDir` is destroyed.
325 ///
326 /// # Errors
327 ///
328 /// If the directory can not be created, `Err` is returned.
329 ///
330 /// # Examples
331 ///
332 /// ```
333 /// use std::fs::{self, File};
334 /// use std::io::Write;
335 /// use tempfile::TempDir;
336 ///
337 /// // Create a directory inside of the current directory
338 /// let tmp_dir = TempDir::with_prefix_in("foo-", ".")?;
339 /// let tmp_name = tmp_dir.path().file_name().unwrap().to_str().unwrap();
340 /// assert!(tmp_name.starts_with("foo-"));
341 /// # Ok::<(), std::io::Error>(())
342 /// ```
343 pub fn with_prefix_in<S: AsRef<OsStr>, P: AsRef<Path>>(
344 prefix: S,
345 dir: P,
346 ) -> io::Result<TempDir> {
347 Builder::new().prefix(&prefix).tempdir_in(dir)
348 }
349
350 /// Accesses the [`Path`] to the temporary directory.
351 ///
352 /// [`Path`]: http://doc.rust-lang.org/std/path/struct.Path.html
353 ///
354 /// # Examples
355 ///
356 /// ```
357 /// use tempfile::TempDir;
358 ///
359 /// let tmp_path;
360 ///
361 /// {
362 /// let tmp_dir = TempDir::new()?;
363 /// tmp_path = tmp_dir.path().to_owned();
364 ///
365 /// // Check that the temp directory actually exists.
366 /// assert!(tmp_path.exists());
367 ///
368 /// // End of `tmp_dir` scope, directory will be deleted
369 /// }
370 ///
371 /// // Temp directory should be deleted by now
372 /// assert_eq!(tmp_path.exists(), false);
373 /// # Ok::<(), std::io::Error>(())
374 /// ```
375 #[must_use]
376 pub fn path(&self) -> &path::Path {
377 self.path.as_ref()
378 }
379
380 /// Persist the temporary directory to disk, returning the [`PathBuf`] where it is located.
381 ///
382 /// This consumes the [`TempDir`] without deleting directory on the filesystem, meaning that
383 /// the directory will no longer be automatically deleted.
384 ///
385 /// [`TempDir`]: struct.TempDir.html
386 /// [`PathBuf`]: http://doc.rust-lang.org/std/path/struct.PathBuf.html
387 ///
388 /// # Examples
389 ///
390 /// ```
391 /// use std::fs;
392 /// use tempfile::TempDir;
393 ///
394 /// let tmp_dir = TempDir::new()?;
395 ///
396 /// // Persist the temporary directory to disk,
397 /// // getting the path where it is.
398 /// let tmp_path = tmp_dir.into_path();
399 ///
400 /// // Delete the temporary directory ourselves.
401 /// fs::remove_dir_all(tmp_path)?;
402 /// # Ok::<(), std::io::Error>(())
403 /// ```
404 #[must_use]
405 pub fn into_path(self) -> PathBuf {
406 // Prevent the Drop impl from being called.
407 let mut this = mem::ManuallyDrop::new(self);
408
409 // replace this.path with an empty Box, since an empty Box does not
410 // allocate any heap memory.
411 mem::replace(&mut this.path, PathBuf::new().into_boxed_path()).into()
412 }
413
414 /// Closes and removes the temporary directory, returning a `Result`.
415 ///
416 /// Although `TempDir` removes the directory on drop, in the destructor
417 /// any errors are ignored. To detect errors cleaning up the temporary
418 /// directory, call `close` instead.
419 ///
420 /// # Errors
421 ///
422 /// This function may return a variety of [`std::io::Error`]s that result from deleting
423 /// the files and directories contained with the temporary directory,
424 /// as well as from deleting the temporary directory itself. These errors
425 /// may be platform specific.
426 ///
427 /// [`std::io::Error`]: http://doc.rust-lang.org/std/io/struct.Error.html
428 ///
429 /// # Examples
430 ///
431 /// ```
432 /// use std::fs::File;
433 /// use std::io::Write;
434 /// use tempfile::TempDir;
435 ///
436 /// // Create a directory inside of `env::temp_dir()`.
437 /// let tmp_dir = TempDir::new()?;
438 /// let file_path = tmp_dir.path().join("my-temporary-note.txt");
439 /// let mut tmp_file = File::create(file_path)?;
440 /// writeln!(tmp_file, "Brian was here. Briefly.")?;
441 ///
442 /// // By closing the `TempDir` explicitly we can check that it has
443 /// // been deleted successfully. If we don't close it explicitly,
444 /// // the directory will still be deleted when `tmp_dir` goes out
445 /// // of scope, but we won't know whether deleting the directory
446 /// // succeeded.
447 /// drop(tmp_file);
448 /// tmp_dir.close()?;
449 /// # Ok::<(), std::io::Error>(())
450 /// ```
451 pub fn close(mut self) -> io::Result<()> {
452 let result = remove_dir_all(self.path()).with_err_path(|| self.path());
453
454 // Set self.path to empty Box to release the memory, since an empty
455 // Box does not allocate any heap memory.
456 self.path = PathBuf::new().into_boxed_path();
457
458 // Prevent the Drop impl from being called.
459 mem::forget(self);
460
461 result
462 }
463}
464
465impl AsRef<Path> for TempDir {
466 fn as_ref(&self) -> &Path {
467 self.path()
468 }
469}
470
471impl fmt::Debug for TempDir {
472 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
473 f.debug_struct("TempDir")
474 .field("path", &self.path())
475 .finish()
476 }
477}
478
479impl Drop for TempDir {
480 fn drop(&mut self) {
481 if !self.keep {
482 let _ = remove_dir_all(self.path());
483 }
484 }
485}
486
487pub(crate) fn create(
488 path: PathBuf,
489 permissions: Option<&std::fs::Permissions>,
490 keep: bool,
491) -> io::Result<TempDir> {
492 imp::create(path, permissions, keep)
493}
494
495mod imp;