mktemp/
temp_file.rs

1use std::env;
2use std::ffi::CString;
3use std::fmt::Arguments;
4use std::fs::{self, File};
5use std::io::{Error, ErrorKind, Read, Result, Seek, SeekFrom, Write};
6
7#[cfg(unix)]
8use std::os::unix::io::FromRawFd;
9#[cfg(unix)]
10use libc::{c_int, mkstemps};
11
12/// A temporary file
13pub struct TempFile {
14    file: Option<File>,
15    path: String,
16}
17
18impl TempFile {
19    /// Creates a new temporary file with the given prefix and suffix.
20    ///
21    /// # Errors
22    ///
23    /// Any of the following will produce errors:
24    /// * A suffix with > `std::i32::MAX` chars
25    /// * Failure to parse a CString from the given data
26    /// * `mkstemps` returning an invalid file descriptor
27    ///
28    /// # Examples
29    ///
30    /// ```
31    /// use std::io::Write;
32    /// use mktemp::TempFile;
33    ///
34    /// let mut tf = TempFile::new("my-groovy-prefix-", ".txt").unwrap();
35    /// tf.write(b"Hello world!");
36    /// ```
37    pub fn new(prefix: &str, suffix: &str) -> Result<TempFile> {
38        debug!("init new TempFile");
39        // validate suffix len
40        let len = suffix.chars().count();
41        if len > std::i32::MAX as usize {
42            return Err(Error::new(
43                ErrorKind::InvalidData,
44                "Suffix length must be less than std::i32::MAX",
45            ));
46        }
47        debug!("validated suffix len");
48
49        // get temporary directory
50        let tmp_dir = env::temp_dir();
51        debug!("found temp dir: {:?}", tmp_dir);
52
53        // CString --> &c_char
54        let ptr = match CString::new(format!("{}/{}XXXXXX{}", tmp_dir.display(), prefix, suffix)) {
55            Ok(p) => p.into_raw(),
56            Err(e) => return Err(Error::new(ErrorKind::Other, e)),
57        };
58        debug!("CString to raw done");
59
60        // get fd
61        let fd = unsafe { mkstemps(ptr, len as c_int) };
62        debug!("got fd: {}", fd);
63
64        // &c_char --> CString --> String
65        let path = match unsafe { CString::from_raw(ptr) }.into_string() {
66            Ok(s) => s,
67            Err(e) => return Err(Error::new(ErrorKind::Other, e)),
68        };
69        debug!("raw to CString to String done");
70        debug!("got file path: {}", &path);
71
72        // handle mkstemps error (if any)
73        if fd < 0 {
74            debug!("fd was error");
75            return Err(Error::last_os_error());
76        }
77
78        // fd --> File
79        let file = unsafe { File::from_raw_fd(fd) };
80        debug!("got file from fd");
81
82        // yay!
83        Ok(TempFile {
84            file: Some(file),
85            path: path,
86        })
87    }
88
89    /// Return the path to the underlying file
90    pub fn path(&self) -> &str {
91        &self.path
92    }
93
94    /// Return the underlying File
95    pub fn inner(&mut self) -> &mut File {
96        self.file.as_mut().unwrap()
97    }
98}
99
100impl Drop for TempFile {
101    fn drop(&mut self) {
102        debug!("Dropping TempFile: {}", &self.path);
103        self.file = None;
104        let _ = fs::remove_file(&self.path);
105    }
106}
107
108impl Seek for TempFile {
109    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
110        self.inner().seek(pos)
111    }
112}
113
114impl Read for TempFile {
115    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
116        self.inner().read(buf)
117    }
118
119    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize> {
120        self.inner().read_to_end(buf)
121    }
122
123    fn read_to_string(&mut self, buf: &mut String) -> Result<usize> {
124        self.inner().read_to_string(buf)
125    }
126
127    fn read_exact(&mut self, buf: &mut [u8]) -> Result<()> {
128        self.inner().read_exact(buf)
129    }
130}
131
132impl Write for TempFile {
133    fn write(&mut self, buf: &[u8]) -> Result<usize> {
134        self.inner().write(buf)
135    }
136
137    fn flush(&mut self) -> Result<()> {
138        self.inner().flush()
139    }
140
141    fn write_all(&mut self, buf: &[u8]) -> Result<()> {
142        self.inner().write_all(buf)
143    }
144
145    fn write_fmt(&mut self, fmt: Arguments<'_>) -> Result<()> {
146        self.inner().write_fmt(fmt)
147    }
148}