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}