1#![forbid(missing_docs)]
16#![forbid(rustdoc::missing_crate_level_docs)]
17#![warn(rustdoc::unescaped_backticks)]
18
19use std::mem::ManuallyDrop;
34
35pub struct TestFile {
47 file : ManuallyDrop<std::fs::File>,
48 path : std::path::PathBuf
49}
50
51impl Drop for TestFile {
57 fn drop(&mut self) {
58 unsafe { ManuallyDrop::drop(&mut self.file) };
59 let _ = std::fs::remove_file(&self.path);
60 }
61}
62
63const RETRIES: u32 = 10;
69
70use std::convert::{AsRef,AsMut};
73
74impl AsRef<std::path::Path> for TestFile {
75 fn as_ref(&self) -> &std::path::Path {
76 &self.path
77 }
78}
79
80impl AsRef<std::path::PathBuf> for TestFile {
81 fn as_ref(&self) -> &std::path::PathBuf {
82 &self.path
83 }
84}
85
86impl AsRef<std::fs::File> for TestFile {
87 fn as_ref(&self) -> &std::fs::File {
88 &self.file
89 }
90}
91
92impl AsMut<std::fs::File> for TestFile {
93 fn as_mut(&mut self) -> &mut std::fs::File {
94 &mut self.file
95 }
96}
97
98impl AsMut<dyn std::io::Read> for TestFile {
99 fn as_mut(&mut self) -> &mut (dyn std::io::Read + 'static) {
100 self.file.deref_mut()
101 }
102}
103
104impl AsMut<dyn std::io::Write> for TestFile {
105 fn as_mut(&mut self) -> &mut (dyn std::io::Write + 'static) {
106 self.file.deref_mut()
107 }
108}
109
110impl AsRef<TestFile> for TestFile {
111 fn as_ref(&self) -> &TestFile {
112 self
113 }
114}
115
116impl AsMut<TestFile> for TestFile {
117 fn as_mut(&mut self) -> &mut TestFile {
118 self
119 }
120}
121
122use std::ops::{Deref,DerefMut};
123
124impl Deref for TestFile {
125 type Target = std::fs::File;
126 fn deref(&self) -> &Self::Target { &self.file }
127}
128
129impl DerefMut for TestFile {
130 fn deref_mut(&mut self) -> &mut Self::Target { &mut self.file }
131}
132
133impl From<String> for TestFile {
139 fn from(content: String) -> Self {
140 self::from(content)
141 }
142}
143
144impl From<&String> for TestFile {
150 fn from(content: &String) -> Self {
151 self::from(content)
152 }
153}
154
155impl From <std::path::PathBuf> for TestFile {
159 fn from(buf: std::path::PathBuf) -> Self {
160 from_file(buf)
161 }
162}
163
164impl From <&std::path::PathBuf> for TestFile {
168 fn from(buf: &std::path::PathBuf) -> Self {
169 from_file(buf)
170 }
171}
172
173
174pub fn empty() -> TestFile {
178 create(|_unused| Ok(()))
179}
180
181pub fn from<T: AsRef<str>>(content: T) -> TestFile {
187 use std::io::Write;
188 create(|tfile| tfile.file.write_all(content.as_ref().as_bytes()))
189}
190
191pub fn from_file(path: impl AsRef<std::path::Path>) -> TestFile {
197 std::fs::OpenOptions::new().read(true).write(true).create_new(false).open(path.as_ref()).map(
198 |f| TestFile{ file:ManuallyDrop::new(f), path:std::path::PathBuf::new() }).map(
199 |mut tf| { tf.path.push(path.as_ref());tf})
200 .unwrap()
201}
202
203pub fn create<I>(initializer: I) -> TestFile
210 where I: Fn (&mut TestFile) -> std::io::Result<()> {
211
212 let mut retries = RETRIES;
213 let mut rng = rand::thread_rng();
214 use std::fs::OpenOptions;
215 use std::io::{Seek, SeekFrom};
216
217 while retries > 0 {
218 retries -= 1;
219 let mut tmp = std::env::temp_dir();
220 tmp.push(format!("TMP{}",generate_random(&mut rng)));
221 match OpenOptions::new().read(true).write(true).create_new(true).open(&tmp).map(
222 |f| TestFile{ file:ManuallyDrop::new(f), path:tmp }
223 ).and_then(|mut tfile:TestFile| -> std::io::Result<TestFile> { match initializer(<TestFile as AsMut<TestFile>>::as_mut(&mut tfile)) { Ok(_) => Ok(tfile), Err(ioerr) => Err(ioerr) }}
224 ).and_then(|mut tfile| match tfile.file.seek(SeekFrom::Start(0)) { Ok(_) => Ok(tfile), Err(ioerr) => Err(ioerr) }
225 ).and_then(|mut tfile| match <dyn std::io::Write>::flush(<TestFile as AsMut<std::fs::File>>::as_mut(&mut tfile)) { Ok(_) => Ok(tfile), Err(ioerr) => Err(ioerr) }
226 ) {
227 Ok(tfile) => return tfile,
228 Err(_) => continue
229 }
230 }
231 panic!("Can't create temporary file")
232}
233
234use rand::prelude::*;
235
236fn generate_random(rng : &mut ThreadRng) -> u32 {
241 rng.gen_range(1000..10000)
242}
243
244pub fn generate_name() -> std::path::PathBuf {
250 let mut retries = RETRIES;
251 let mut rng = rand::thread_rng();
252 use std::fs::OpenOptions;
253
254 while retries > 0 {
255 retries -= 1;
256 let mut tmp = std::env::temp_dir();
257 tmp.push(format!("TMP{}", generate_random(&mut rng)));
258 match OpenOptions::new().read(true).write(true).create_new(true).open(&tmp).map(
259 |f| { drop(f); let _ = std::fs::remove_file(&tmp); })
260 {
261 Ok(()) => return tmp,
262 Err(_) => continue
263 }
264 }
265 panic!("Can't generate suitable tmp file name");
266}
267
268#[cfg(test)]
269#[path= "./lib_tests.rs"]
270mod tests;