mini/fs.rs
1/*
2 * Copyright (c) 2018 Adgear
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a copy of
5 * this software and associated documentation files (the "Software"), to deal in
6 * the Software without restriction, including without limitation the rights to
7 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
8 * the Software, and to permit persons to whom the Software is furnished to do so,
9 * subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in all
12 * copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
17 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
18 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
19 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 */
21
22//! Provide file-system helpers like a way to create a temporary file.
23
24use std::env::temp_dir;
25use std::fs::{File, OpenOptions, remove_file};
26use std::io;
27use std::os::unix::fs::OpenOptionsExt;
28use std::path::PathBuf;
29
30use crate::rand::Rng;
31
32/// A temporary file which is deleted when it goes out of scope.
33pub struct TempFile {
34 file: File,
35 path: PathBuf,
36}
37
38impl TempFile {
39 /// Creates a new temporary file with a default prefix.
40 pub fn new() -> io::Result<Self> {
41 Self::with_prefix("file")
42 }
43
44 /// Gets the file handle of the temporary file.
45 pub fn get(&self) -> &File {
46 &self.file
47 }
48
49 /// Creates a new temporary file with the specified prefix.
50 pub fn with_prefix(prefix: &str) -> io::Result<Self> {
51 let mut rng = Rng::new();
52 for _ in 0..50 {
53 let tempfile = temp_dir()
54 .join(format!("{}.{}", prefix, rng.gen_int()));
55 if let Ok(file) = OpenOptions::new()
56 .read(true)
57 .write(true)
58 .create_new(true)
59 .mode(0o600)
60 .open(&tempfile)
61 {
62 return Ok(Self {
63 file,
64 path: tempfile,
65 })
66 }
67 }
68 Err(io::Error::from(io::ErrorKind::AlreadyExists))
69 }
70}
71
72impl Drop for TempFile {
73 fn drop(&mut self) {
74 if let Err(error) = remove_file(&self.path) {
75 eprintln!("Cannot remove file: {}", error);
76 }
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use std::io::Write;
83
84 use super::TempFile;
85
86 #[test]
87 fn test_temp_file_exists() {
88 let path;
89 {
90 let temp_file = TempFile::new().expect("new temp file");
91 path = temp_file.path.clone();
92 assert!(path.is_file());
93 writeln!(temp_file.get(), "test").expect("write");
94 }
95 assert!(!path.is_file());
96 assert!(!path.exists());
97
98 let path;
99 {
100 let temp_file = TempFile::with_prefix("mini-prefix").expect("new temp file");
101 path = temp_file.path.clone();
102 assert!(path.is_file());
103 }
104 assert!(!path.is_file());
105 assert!(!path.exists());
106 }
107}