use std::io::{self, Result}; use std::path::{Path, PathBuf}; use rand::{Rng, distributions::Alphanumeric}; use tokio::fs; /// Create a temporary file named `temp.[a-zA-Z0-9]{length}` in /// the given location and immediately open it. Returns the /// filename and the corresponding file handle. It is the caller's /// responsibility to clean up the temporary file when it is no /// longer needed. /// /// Uses [`OpenOptions::create_new`][fs::OpenOptions::create_new] /// to detect filename collisions, in which case it will /// automatically retry until the operation succeeds. /// /// # Errors /// /// Returns the underlying [`io::Error`] if the operation fails /// due to reasons other than filename collision. pub async fn mktemp(dir: T, basename: B, length: usize) -> Result<(PathBuf, fs::File)> where T: AsRef, B: Into> { let dir = dir.as_ref(); let basename = basename.into().unwrap_or(""); fs::create_dir_all(dir).await?; loop { let filename = dir.join(format!( "{}{}{}", basename, if basename.is_empty() { "" } else { "." }, { let string = rand::thread_rng() .sample_iter(&Alphanumeric) .take(length) .collect::>(); String::from_utf8(string).unwrap() } )); match fs::OpenOptions::new() .create_new(true) .write(true) .open(&filename) .await { Ok(file) => return Ok((filename, file)), Err(err) => match err.kind() { io::ErrorKind::AlreadyExists => continue, _ => return Err(err) } } } }