Coverage Report

Created: 2026-06-30 06:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tempfile-3.3.0/src/lib.rs
Line
Count
Source
1
//! Temporary files and directories.
2
//!
3
//! - Use the [`tempfile()`] function for temporary files
4
//! - Use the [`tempdir()`] function for temporary directories.
5
//!
6
//! # Design
7
//!
8
//! This crate provides several approaches to creating temporary files and directories.
9
//! [`tempfile()`] relies on the OS to remove the temporary file once the last handle is closed.
10
//! [`TempDir`] and [`NamedTempFile`] both rely on Rust destructors for cleanup.
11
//!
12
//! When choosing between the temporary file variants, prefer `tempfile`
13
//! unless you either need to know the file's path or to be able to persist it.
14
//!
15
//! ## Resource Leaking
16
//!
17
//! `tempfile` will (almost) never fail to cleanup temporary resources, but `TempDir` and `NamedTempFile` will if
18
//! their destructors don't run. This is because `tempfile` relies on the OS to cleanup the
19
//! underlying file, while `TempDir` and `NamedTempFile` rely on their destructors to do so.
20
//!
21
//! ## Security
22
//!
23
//! In the presence of pathological temporary file cleaner, relying on file paths is unsafe because
24
//! a temporary file cleaner could delete the temporary file which an attacker could then replace.
25
//!
26
//! `tempfile` doesn't rely on file paths so this isn't an issue. However, `NamedTempFile` does
27
//! rely on file paths for _some_ operations. See the security documentation on
28
//! the `NamedTempFile` type for more information.
29
//!
30
//! ## Early drop pitfall
31
//!
32
//! Because `TempDir` and `NamedTempFile` rely on their destructors for cleanup, this can lead
33
//! to an unexpected early removal of the directory/file, usually when working with APIs which are
34
//! generic over `AsRef<Path>`. Consider the following example:
35
//!
36
//! ```no_run
37
//! # use tempfile::tempdir;
38
//! # use std::io;
39
//! # use std::process::Command;
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 temp_dir = tempdir()?;
48
//!
49
//! // Spawn the `touch` command inside the temporary directory and collect the exit status
50
//! // Note that `temp_dir` is **not** moved into `current_dir`, but passed as a reference
51
//! let exit_status = Command::new("touch").arg("tmp").current_dir(&temp_dir).status()?;
52
//! assert!(exit_status.success());
53
//!
54
//! # Ok(())
55
//! # }
56
//! ```
57
//!
58
//! This works because a reference to `temp_dir` is passed to `current_dir`, resulting in the
59
//! destructor of `temp_dir` being run after the `Command` has finished execution. Moving the
60
//! `TempDir` into the `current_dir` call would result in the `TempDir` being converted into
61
//! an internal representation, with the original value being dropped and the directory thus
62
//! being deleted, before the command can be executed.
63
//!
64
//! The `touch` command would fail with an `No such file or directory` error.
65
//!
66
//! ## Examples
67
//!
68
//! Create a temporary file and write some data into it:
69
//!
70
//! ```
71
//! use tempfile::tempfile;
72
//! use std::io::{self, Write};
73
//!
74
//! # fn main() {
75
//! #     if let Err(_) = run() {
76
//! #         ::std::process::exit(1);
77
//! #     }
78
//! # }
79
//! # fn run() -> Result<(), io::Error> {
80
//! // Create a file inside of `std::env::temp_dir()`.
81
//! let mut file = tempfile()?;
82
//!
83
//! writeln!(file, "Brian was here. Briefly.")?;
84
//! # Ok(())
85
//! # }
86
//! ```
87
//!
88
//! Create a named temporary file and open an independent file handle:
89
//!
90
//! ```
91
//! use tempfile::NamedTempFile;
92
//! use std::io::{self, Write, Read};
93
//!
94
//! # fn main() {
95
//! #     if let Err(_) = run() {
96
//! #         ::std::process::exit(1);
97
//! #     }
98
//! # }
99
//! # fn run() -> Result<(), io::Error> {
100
//! let text = "Brian was here. Briefly.";
101
//!
102
//! // Create a file inside of `std::env::temp_dir()`.
103
//! let mut file1 = NamedTempFile::new()?;
104
//!
105
//! // Re-open it.
106
//! let mut file2 = file1.reopen()?;
107
//!
108
//! // Write some test data to the first handle.
109
//! file1.write_all(text.as_bytes())?;
110
//!
111
//! // Read the test data using the second handle.
112
//! let mut buf = String::new();
113
//! file2.read_to_string(&mut buf)?;
114
//! assert_eq!(buf, text);
115
//! # Ok(())
116
//! # }
117
//! ```
118
//!
119
//! Create a temporary directory and add a file to it:
120
//!
121
//! ```
122
//! use tempfile::tempdir;
123
//! use std::fs::File;
124
//! use std::io::{self, Write};
125
//!
126
//! # fn main() {
127
//! #     if let Err(_) = run() {
128
//! #         ::std::process::exit(1);
129
//! #     }
130
//! # }
131
//! # fn run() -> Result<(), io::Error> {
132
//! // Create a directory inside of `std::env::temp_dir()`.
133
//! let dir = tempdir()?;
134
//!
135
//! let file_path = dir.path().join("my-temporary-note.txt");
136
//! let mut file = File::create(file_path)?;
137
//! writeln!(file, "Brian was here. Briefly.")?;
138
//!
139
//! // By closing the `TempDir` explicitly, we can check that it has
140
//! // been deleted successfully. If we don't close it explicitly,
141
//! // the directory will still be deleted when `dir` goes out
142
//! // of scope, but we won't know whether deleting the directory
143
//! // succeeded.
144
//! drop(file);
145
//! dir.close()?;
146
//! # Ok(())
147
//! # }
148
//! ```
149
//!
150
//! [`tempfile()`]: fn.tempfile.html
151
//! [`tempdir()`]: fn.tempdir.html
152
//! [`TempDir`]: struct.TempDir.html
153
//! [`NamedTempFile`]: struct.NamedTempFile.html
154
//! [`std::env::temp_dir()`]: https://doc.rust-lang.org/std/env/fn.temp_dir.html
155
156
#![doc(
157
    html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
158
    html_favicon_url = "https://www.rust-lang.org/favicon.ico",
159
    html_root_url = "https://docs.rs/tempfile/3.1.0"
160
)]
161
#![cfg_attr(test, deny(warnings))]
162
#![deny(rust_2018_idioms)]
163
#![allow(clippy::redundant_field_names)]
164
#![cfg_attr(feature = "nightly", feature(wasi_ext))]
165
166
#[cfg(doctest)]
167
doc_comment::doctest!("../README.md");
168
169
const NUM_RETRIES: u32 = 1 << 31;
170
const NUM_RAND_CHARS: usize = 6;
171
172
use std::ffi::OsStr;
173
use std::fs::OpenOptions;
174
use std::path::Path;
175
use std::{env, io};
176
177
mod dir;
178
mod error;
179
mod file;
180
mod spooled;
181
mod util;
182
183
pub use crate::dir::{tempdir, tempdir_in, TempDir};
184
pub use crate::file::{
185
    tempfile, tempfile_in, NamedTempFile, PathPersistError, PersistError, TempPath,
186
};
187
pub use crate::spooled::{spooled_tempfile, SpooledTempFile};
188
189
/// Create a new temporary file or directory with custom parameters.
190
#[derive(Debug, Clone, Eq, PartialEq)]
191
pub struct Builder<'a, 'b> {
192
    random_len: usize,
193
    prefix: &'a OsStr,
194
    suffix: &'b OsStr,
195
    append: bool,
196
}
197
198
impl<'a, 'b> Default for Builder<'a, 'b> {
199
0
    fn default() -> Self {
200
0
        Builder {
201
0
            random_len: crate::NUM_RAND_CHARS,
202
0
            prefix: OsStr::new(".tmp"),
203
0
            suffix: OsStr::new(""),
204
0
            append: false,
205
0
        }
206
0
    }
207
}
208
209
impl<'a, 'b> Builder<'a, 'b> {
210
    /// Create a new `Builder`.
211
    ///
212
    /// # Examples
213
    ///
214
    /// Create a named temporary file and write some data into it:
215
    ///
216
    /// ```
217
    /// # use std::io;
218
    /// # use std::ffi::OsStr;
219
    /// # fn main() {
220
    /// #     if let Err(_) = run() {
221
    /// #         ::std::process::exit(1);
222
    /// #     }
223
    /// # }
224
    /// # fn run() -> Result<(), io::Error> {
225
    /// use tempfile::Builder;
226
    ///
227
    /// let named_tempfile = Builder::new()
228
    ///     .prefix("my-temporary-note")
229
    ///     .suffix(".txt")
230
    ///     .rand_bytes(5)
231
    ///     .tempfile()?;
232
    ///
233
    /// let name = named_tempfile
234
    ///     .path()
235
    ///     .file_name().and_then(OsStr::to_str);
236
    ///
237
    /// if let Some(name) = name {
238
    ///     assert!(name.starts_with("my-temporary-note"));
239
    ///     assert!(name.ends_with(".txt"));
240
    ///     assert_eq!(name.len(), "my-temporary-note.txt".len() + 5);
241
    /// }
242
    /// # Ok(())
243
    /// # }
244
    /// ```
245
    ///
246
    /// Create a temporary directory and add a file to it:
247
    ///
248
    /// ```
249
    /// # use std::io::{self, Write};
250
    /// # use std::fs::File;
251
    /// # use std::ffi::OsStr;
252
    /// # fn main() {
253
    /// #     if let Err(_) = run() {
254
    /// #         ::std::process::exit(1);
255
    /// #     }
256
    /// # }
257
    /// # fn run() -> Result<(), io::Error> {
258
    /// use tempfile::Builder;
259
    ///
260
    /// let dir = Builder::new()
261
    ///     .prefix("my-temporary-dir")
262
    ///     .rand_bytes(5)
263
    ///     .tempdir()?;
264
    ///
265
    /// let file_path = dir.path().join("my-temporary-note.txt");
266
    /// let mut file = File::create(file_path)?;
267
    /// writeln!(file, "Brian was here. Briefly.")?;
268
    ///
269
    /// // By closing the `TempDir` explicitly, we can check that it has
270
    /// // been deleted successfully. If we don't close it explicitly,
271
    /// // the directory will still be deleted when `dir` goes out
272
    /// // of scope, but we won't know whether deleting the directory
273
    /// // succeeded.
274
    /// drop(file);
275
    /// dir.close()?;
276
    /// # Ok(())
277
    /// # }
278
    /// ```
279
0
    pub fn new() -> Self {
280
0
        Self::default()
281
0
    }
282
283
    /// Set a custom filename prefix.
284
    ///
285
    /// Path separators are legal but not advisable.
286
    /// Default: `.tmp`.
287
    ///
288
    /// # Examples
289
    ///
290
    /// ```
291
    /// # use std::io;
292
    /// # fn main() {
293
    /// #     if let Err(_) = run() {
294
    /// #         ::std::process::exit(1);
295
    /// #     }
296
    /// # }
297
    /// # fn run() -> Result<(), io::Error> {
298
    /// # use tempfile::Builder;
299
    /// let named_tempfile = Builder::new()
300
    ///     .prefix("my-temporary-note")
301
    ///     .tempfile()?;
302
    /// # Ok(())
303
    /// # }
304
    /// ```
305
0
    pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self {
306
0
        self.prefix = prefix.as_ref();
307
0
        self
308
0
    }
309
310
    /// Set a custom filename suffix.
311
    ///
312
    /// Path separators are legal but not advisable.
313
    /// Default: empty.
314
    ///
315
    /// # Examples
316
    ///
317
    /// ```
318
    /// # use std::io;
319
    /// # fn main() {
320
    /// #     if let Err(_) = run() {
321
    /// #         ::std::process::exit(1);
322
    /// #     }
323
    /// # }
324
    /// # fn run() -> Result<(), io::Error> {
325
    /// # use tempfile::Builder;
326
    /// let named_tempfile = Builder::new()
327
    ///     .suffix(".txt")
328
    ///     .tempfile()?;
329
    /// # Ok(())
330
    /// # }
331
    /// ```
332
0
    pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self {
333
0
        self.suffix = suffix.as_ref();
334
0
        self
335
0
    }
336
337
    /// Set the number of random bytes.
338
    ///
339
    /// Default: `6`.
340
    ///
341
    /// # Examples
342
    ///
343
    /// ```
344
    /// # use std::io;
345
    /// # fn main() {
346
    /// #     if let Err(_) = run() {
347
    /// #         ::std::process::exit(1);
348
    /// #     }
349
    /// # }
350
    /// # fn run() -> Result<(), io::Error> {
351
    /// # use tempfile::Builder;
352
    /// let named_tempfile = Builder::new()
353
    ///     .rand_bytes(5)
354
    ///     .tempfile()?;
355
    /// # Ok(())
356
    /// # }
357
    /// ```
358
0
    pub fn rand_bytes(&mut self, rand: usize) -> &mut Self {
359
0
        self.random_len = rand;
360
0
        self
361
0
    }
362
363
    /// Set the file to be opened in append mode.
364
    ///
365
    /// Default: `false`.
366
    ///
367
    /// # Examples
368
    ///
369
    /// ```
370
    /// # use std::io;
371
    /// # fn main() {
372
    /// #     if let Err(_) = run() {
373
    /// #         ::std::process::exit(1);
374
    /// #     }
375
    /// # }
376
    /// # fn run() -> Result<(), io::Error> {
377
    /// # use tempfile::Builder;
378
    /// let named_tempfile = Builder::new()
379
    ///     .append(true)
380
    ///     .tempfile()?;
381
    /// # Ok(())
382
    /// # }
383
    /// ```
384
0
    pub fn append(&mut self, append: bool) -> &mut Self {
385
0
        self.append = append;
386
0
        self
387
0
    }
388
389
    /// Create the named temporary file.
390
    ///
391
    /// # Security
392
    ///
393
    /// See [the security][security] docs on `NamedTempFile`.
394
    ///
395
    /// # Resource leaking
396
    ///
397
    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
398
    ///
399
    /// # Errors
400
    ///
401
    /// If the file cannot be created, `Err` is returned.
402
    ///
403
    /// # Examples
404
    ///
405
    /// ```
406
    /// # use std::io;
407
    /// # fn main() {
408
    /// #     if let Err(_) = run() {
409
    /// #         ::std::process::exit(1);
410
    /// #     }
411
    /// # }
412
    /// # fn run() -> Result<(), io::Error> {
413
    /// # use tempfile::Builder;
414
    /// let tempfile = Builder::new().tempfile()?;
415
    /// # Ok(())
416
    /// # }
417
    /// ```
418
    ///
419
    /// [security]: struct.NamedTempFile.html#security
420
    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
421
0
    pub fn tempfile(&self) -> io::Result<NamedTempFile> {
422
0
        self.tempfile_in(&env::temp_dir())
423
0
    }
424
425
    /// Create the named temporary file in the specified directory.
426
    ///
427
    /// # Security
428
    ///
429
    /// See [the security][security] docs on `NamedTempFile`.
430
    ///
431
    /// # Resource leaking
432
    ///
433
    /// See [the resource leaking][resource-leaking] docs on `NamedTempFile`.
434
    ///
435
    /// # Errors
436
    ///
437
    /// If the file cannot be created, `Err` is returned.
438
    ///
439
    /// # Examples
440
    ///
441
    /// ```
442
    /// # use std::io;
443
    /// # fn main() {
444
    /// #     if let Err(_) = run() {
445
    /// #         ::std::process::exit(1);
446
    /// #     }
447
    /// # }
448
    /// # fn run() -> Result<(), io::Error> {
449
    /// # use tempfile::Builder;
450
    /// let tempfile = Builder::new().tempfile_in("./")?;
451
    /// # Ok(())
452
    /// # }
453
    /// ```
454
    ///
455
    /// [security]: struct.NamedTempFile.html#security
456
    /// [resource-leaking]: struct.NamedTempFile.html#resource-leaking
457
0
    pub fn tempfile_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<NamedTempFile> {
458
0
        util::create_helper(
459
0
            dir.as_ref(),
460
0
            self.prefix,
461
0
            self.suffix,
462
0
            self.random_len,
463
0
            |path| file::create_named(path, OpenOptions::new().append(self.append)),
464
        )
465
0
    }
466
467
    /// Attempts to make a temporary directory inside of `env::temp_dir()` whose
468
    /// name will have the prefix, `prefix`. The directory and
469
    /// everything inside it will be automatically deleted once the
470
    /// returned `TempDir` is destroyed.
471
    ///
472
    /// # Resource leaking
473
    ///
474
    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
475
    ///
476
    /// # Errors
477
    ///
478
    /// If the directory can not be created, `Err` is returned.
479
    ///
480
    /// # Examples
481
    ///
482
    /// ```
483
    /// use std::fs::File;
484
    /// use std::io::Write;
485
    /// use tempfile::Builder;
486
    ///
487
    /// # use std::io;
488
    /// # fn run() -> Result<(), io::Error> {
489
    /// let tmp_dir = Builder::new().tempdir()?;
490
    /// # Ok(())
491
    /// # }
492
    /// ```
493
    ///
494
    /// [resource-leaking]: struct.TempDir.html#resource-leaking
495
0
    pub fn tempdir(&self) -> io::Result<TempDir> {
496
0
        self.tempdir_in(&env::temp_dir())
497
0
    }
498
499
    /// Attempts to make a temporary directory inside of `dir`.
500
    /// The directory and everything inside it will be automatically
501
    /// deleted once the returned `TempDir` is destroyed.
502
    ///
503
    /// # Resource leaking
504
    ///
505
    /// See [the resource leaking][resource-leaking] docs on `TempDir`.
506
    ///
507
    /// # Errors
508
    ///
509
    /// If the directory can not be created, `Err` is returned.
510
    ///
511
    /// # Examples
512
    ///
513
    /// ```
514
    /// use std::fs::{self, File};
515
    /// use std::io::Write;
516
    /// use tempfile::Builder;
517
    ///
518
    /// # use std::io;
519
    /// # fn run() -> Result<(), io::Error> {
520
    /// let tmp_dir = Builder::new().tempdir_in("./")?;
521
    /// # Ok(())
522
    /// # }
523
    /// ```
524
    ///
525
    /// [resource-leaking]: struct.TempDir.html#resource-leaking
526
0
    pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> {
527
        let storage;
528
0
        let mut dir = dir.as_ref();
529
0
        if !dir.is_absolute() {
530
0
            let cur_dir = env::current_dir()?;
531
0
            storage = cur_dir.join(dir);
532
0
            dir = &storage;
533
0
        }
534
535
0
        util::create_helper(dir, self.prefix, self.suffix, self.random_len, dir::create)
536
0
    }
537
}