1#![cfg_attr(target_family = "wasm", allow(unused))]
2
3use std::{
4 collections::hash_map::RandomState,
5 fs::{remove_file, File, OpenOptions},
6 hash::{BuildHasher, Hasher},
7 io, os,
8 path::{Path, PathBuf},
9};
10
11#[cfg(not(any(unix, target_family = "wasm", windows)))]
12compile_error!("Your system is not supported since cc cannot create named tempfile");
13
14fn rand() -> u64 {
15 RandomState::new().build_hasher().finish()
16}
17
18fn tmpname(suffix: &str) -> String {
19 format!("{}{}", rand(), suffix)
20}
21
22fn create_named(path: &Path) -> io::Result<File> {
23 let mut open_options = OpenOptions::new();
24
25 open_options.read(true).write(true).create_new(true);
26
27 #[cfg(all(unix, not(target_os = "wasi")))]
28 <OpenOptions as os::unix::fs::OpenOptionsExt>::mode(&mut open_options, 0o600);
29
30 #[cfg(windows)]
31 <OpenOptions as os::windows::fs::OpenOptionsExt>::custom_flags(
32 &mut open_options,
33 crate::windows::windows_sys::FILE_ATTRIBUTE_TEMPORARY,
34 );
35
36 open_options.open(path)
37}
38
39pub(super) struct NamedTempfile {
40 path: PathBuf,
41 file: Option<File>,
42}
43
44impl NamedTempfile {
45 pub(super) fn new(base: &Path, suffix: &str) -> io::Result<Self> {
46 for _ in 0..10 {
47 let path = base.join(tmpname(suffix));
48 match create_named(&path) {
49 Ok(file) => {
50 return Ok(Self {
51 file: Some(file),
52 path,
53 })
54 }
55 Err(e) if e.kind() == io::ErrorKind::AlreadyExists => continue,
56 Err(e) => return Err(e),
57 };
58 }
59
60 Err(io::Error::new(
61 io::ErrorKind::AlreadyExists,
62 format!(
63 "too many temporary files exist in base `{}` with suffix `{}`",
64 base.display(),
65 suffix
66 ),
67 ))
68 }
69
70 pub(super) fn path(&self) -> &Path {
71 &self.path
72 }
73
74 pub(super) fn take_file(&mut self) -> Option<File> {
75 self.file.take()
76 }
77}
78
79impl Drop for NamedTempfile {
80 fn drop(&mut self) {
81 self.file.take();
84 let _ = remove_file(&self.path);
85 }
86}