1 | use std::ffi::{OsStr, OsString}; |
2 | use std::path::{Path, PathBuf}; |
3 | use std::{io, iter::repeat_with}; |
4 | |
5 | use crate::error::IoResultExt; |
6 | |
7 | fn tmpname(prefix: &OsStr, suffix: &OsStr, rand_len: usize) -> OsString { |
8 | let mut buf = OsString::with_capacity(prefix.len() + suffix.len() + rand_len); |
9 | buf.push(prefix); |
10 | let mut char_buf = [0u8; 4]; |
11 | for c in repeat_with(fastrand::alphanumeric).take(rand_len) { |
12 | buf.push(c.encode_utf8(&mut char_buf)); |
13 | } |
14 | buf.push(suffix); |
15 | buf |
16 | } |
17 | |
18 | pub fn create_helper<R>( |
19 | base: &Path, |
20 | prefix: &OsStr, |
21 | suffix: &OsStr, |
22 | random_len: usize, |
23 | mut f: impl FnMut(PathBuf) -> io::Result<R>, |
24 | ) -> io::Result<R> { |
25 | let num_retries = if random_len != 0 { |
26 | crate::NUM_RETRIES |
27 | } else { |
28 | 1 |
29 | }; |
30 | |
31 | for _ in 0..num_retries { |
32 | let path = base.join(tmpname(prefix, suffix, random_len)); |
33 | return match f(path) { |
34 | Err(ref e) if e.kind() == io::ErrorKind::AlreadyExists && num_retries > 1 => continue, |
35 | // AddrInUse can happen if we're creating a UNIX domain socket and |
36 | // the path already exists. |
37 | Err(ref e) if e.kind() == io::ErrorKind::AddrInUse && num_retries > 1 => continue, |
38 | res => res, |
39 | }; |
40 | } |
41 | |
42 | Err(io::Error::new( |
43 | io::ErrorKind::AlreadyExists, |
44 | "too many temporary files exist" , |
45 | )) |
46 | .with_err_path(|| base) |
47 | } |
48 | |