#![cfg_attr(target_family = "wasm", allow(unused))] use std::{ collections::hash_map::RandomState, fs::{remove_file, File, OpenOptions}, hash::{BuildHasher, Hasher}, io, os, path::{Path, PathBuf}, }; #[cfg(not(any(unix, target_family = "wasm", windows)))] compile_error!("Your system is not supported since cc cannot create named tempfile"); fn rand() -> u64 { RandomState::new().build_hasher().finish() } fn tmpname(suffix: &str) -> String { format!("{}{}", rand(), suffix) } fn create_named(path: &Path) -> io::Result { let mut open_options = OpenOptions::new(); open_options.read(true).write(true).create_new(true); #[cfg(all(unix, not(target_os = "wasi")))] ::mode(&mut open_options, 0o600); #[cfg(windows)] ::custom_flags( &mut open_options, crate::windows::windows_sys::FILE_ATTRIBUTE_TEMPORARY, ); open_options.open(path) } pub(super) struct NamedTempfile { path: PathBuf, file: Option, } impl NamedTempfile { pub(super) fn new(base: &Path, suffix: &str) -> io::Result { for _ in 0..10 { let path = base.join(tmpname(suffix)); match create_named(&path) { Ok(file) => { return Ok(Self { file: Some(file), path, }) } Err(e) if e.kind() == io::ErrorKind::AlreadyExists => continue, Err(e) => return Err(e), }; } Err(io::Error::new( io::ErrorKind::AlreadyExists, format!( "too many temporary files exist in base `{}` with suffix `{}`", base.display(), suffix ), )) } pub(super) fn path(&self) -> &Path { &self.path } pub(super) fn take_file(&mut self) -> Option { self.file.take() } } impl Drop for NamedTempfile { fn drop(&mut self) { // On Windows you have to close all handle to it before // removing the file. self.file.take(); let _ = remove_file(&self.path); } }