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