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