| 1 | #![ cfg_attr(target_family = "wasm", allow(unused))] | 
| 2 |  | 
|---|
| 3 | use 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)))] | 
|---|
| 12 | compile_error!( "Your system is not supported since cc cannot create named tempfile"); | 
|---|
| 13 |  | 
|---|
| 14 | fn rand() -> u64 { | 
|---|
| 15 | RandomState::new().build_hasher().finish() | 
|---|
| 16 | } | 
|---|
| 17 |  | 
|---|
| 18 | fn tmpname(suffix: &str) -> String { | 
|---|
| 19 | format!( "{}{} ", rand(), suffix) | 
|---|
| 20 | } | 
|---|
| 21 |  | 
|---|
| 22 | fn create_named(path: &Path) -> io::Result<File> { | 
|---|
| 23 | let mut open_options: OpenOptions = 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, mode: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 |  | 
|---|
| 39 | pub(super) struct NamedTempfile { | 
|---|
| 40 | path: PathBuf, | 
|---|
| 41 | file: Option<File>, | 
|---|
| 42 | } | 
|---|
| 43 |  | 
|---|
| 44 | impl 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 |  | 
|---|
| 79 | impl Drop for NamedTempfile { | 
|---|
| 80 | fn drop(&mut self) { | 
|---|
| 81 | // On Windows you have to close all handle to it before | 
|---|
| 82 | // removing the file. | 
|---|
| 83 | self.file.take(); | 
|---|
| 84 | let _ = remove_file(&self.path); | 
|---|
| 85 | } | 
|---|
| 86 | } | 
|---|
| 87 |  | 
|---|