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