1#![forbid(missing_docs)]
16#![forbid(rustdoc::missing_crate_level_docs)]
17#![forbid(non_fmt_panics)]
18#![warn(rustdoc::unescaped_backticks)]
19
20fn generate_random() -> u32 {
25 let random_generator = stdrandom::fast_u64;
27 stdrandom::gen_range(10000..=99999, random_generator)
28}
29
30const FILE_RETRIES: u32 = 30;
36
37const GEN_RETRIES: u32 = 5;
43
44#[cfg(target_family = "unix")]
46fn filename_prefix() -> &'static str {
47 "tmp"
48}
49
50#[cfg(not(target_family = "unix"))]
52fn filename_prefix() -> &'static str {
53 "TMP"
54}
55
56#[cfg(target_family = "unix")]
58fn dirname_prefix() -> &'static str {
59 "tmpdir"
60}
61
62#[cfg(not(target_family = "unix"))]
64fn dirname_prefix() -> &'static str {
65 "TMPDIR"
66}
67
68pub fn generate_name() -> std::path::PathBuf {
75 generate_name_in(std::env::temp_dir())
76}
77
78pub fn generate_dirname() -> std::path::PathBuf {
85 generate_dirname_in(std::env::temp_dir())
86}
87
88pub fn generate_name_in<P: AsRef<std::path::Path>>(dir: P) -> std::path::PathBuf {
94 let closure = || {
95 std::path::PathBuf::from(format!("{}{}", filename_prefix(), generate_random()))
96 };
97 filename_generator(dir, closure)
98}
99
100pub fn generate_dirname_in<P: AsRef<std::path::Path>>(dir: P) -> std::path::PathBuf {
106 let closure = || {
107 std::path::PathBuf::from(format!("{}{}", dirname_prefix(), generate_random()))
108 };
109 filename_generator(dir, closure)
110}
111
112pub fn filename_generator<P1, F, P2>(dir: P1, mut template: F) -> std::path::PathBuf
120 where
121 P1: AsRef<std::path::Path>,
122 F: FnMut() -> P2,
123 P2: AsRef<std::path::Path>,
124{
125 use std::fs::OpenOptions;
126 let mut retries = FILE_RETRIES;
127 let base_dir = dir.as_ref();
128
129 if !base_dir.exists() || !base_dir.is_dir() {
131 panic!("Can't generate temporary name in non existing directory");
132 }
133
134 if std::fs::metadata(base_dir).map(|m| m.permissions().readonly()).unwrap_or(true) {
136 panic!("Directory is not writable.");
137 }
138
139 while retries > 0 {
140 retries -= 1;
141 let mut tmp = base_dir.to_path_buf();
142 tmp.push(template().as_ref());
143
144 match OpenOptions::new().read(true).write(true).create_new(true).open(&tmp) {
145 Ok(f) => {
146 drop(f); let _ = std::fs::remove_file(&tmp); return tmp;
149 }
150 Err(_) => continue,
151 }
152 }
153
154 panic!("Failed to generate a suitable temporary name");
155}
156
157
158include!("lib_testfile.rs");
159include!("lib_testdir.rs");
160include!("lib_sharedtestdir.rs");
161
162#[cfg(test)]
163#[path= "./lib_tests.rs"]
164mod tests;
165
166#[path= "./lib_content.rs"]
173pub mod content;