1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
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<T, B>(dir: T, basename: B, length: usize) -> Result<(PathBuf, fs::File)>
where
T: AsRef<Path>,
B: Into<Option<&'static str>>
{
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::<Vec<u8>>();
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)
}
}
}
}
|