1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
//! Safe wrapper over mkstemp function from libc
//!
//! Usage example:
//!
//! ```rust
//! use std::io::Write;
//! extern crate mkstemp;
//! pub fn main() {
//!     // delete automatically when it goes out of scope
//!     let mut temp_file = mkstemp::TempFile::new("/tmp/testXXXXXX", true).unwrap();
//!     temp_file.write("test content".as_bytes()).unwrap();
//! }
//! ```

use std::io::{self, Read, Write};
use std::fs::{File, remove_file};
use std::os::unix::io::FromRawFd;
use std::ffi::CString;
use std::fmt::Arguments;

extern crate libc;

/// Temporary file
pub struct TempFile {
    // we use Option here as a trick to close the file in the drop trait
    file: Option<File>,
    path: String,
    auto_delete: bool
}

impl TempFile {
    /// Create temporary file
    ///
    /// * `template` - file template as described in mkstemp(3)<br/>
    /// * `auto_delete` - if true the file will be automatically deleted when it goes out of scope<br/>
    pub fn new(template: &str, auto_delete: bool) -> io::Result<TempFile> {
        let ptr = CString::new(template)?.into_raw();
        let fd = unsafe { libc::mkstemp(ptr) };
        let path = unsafe { CString::from_raw(ptr) };

        if fd < 0 {
            return Err(io::Error::last_os_error())
        }

        let file = unsafe { File::from_raw_fd(fd) };

        Ok(TempFile {
            file: Some(file),
            path: path.into_string().map_err(|e| io::Error::new(io::ErrorKind::Other, e))?,
            auto_delete: auto_delete
        })
    }

    /// Return a reference to the actual file path
    pub fn path(&self) -> &str {
        &self.path
    }

    /// Return a mutable reference to inner file
    pub fn inner(&mut self) -> &mut File {
        self.file.as_mut().unwrap()
    }
}

impl Drop for TempFile {
    fn drop(&mut self) {
        // close the file
        self.file = None;
        if self.auto_delete {
            let _ = remove_file(&self.path);
        }
    }
}

impl Read for TempFile {
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        self.inner().read(buf)
    }

    fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> {
        self.inner().read_to_end(buf)
    }

    fn read_to_string(&mut self, buf: &mut String) -> io::Result<usize> {
        self.inner().read_to_string(buf)
    }

    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
        self.inner().read_exact(buf)
    }
}

impl Write for TempFile {
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.inner().write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.inner().flush()
    }

    fn write_all(&mut self, buf: &[u8]) -> io::Result<()> {
        self.inner().write_all(buf)
    }

    fn write_fmt(&mut self, fmt: Arguments) -> io::Result<()> {
        self.inner().write_fmt(fmt)
    }
}