use std::env::temp_dir;
use std::ffi::{CString, OsStr};
use std::fs;
use std::fs::File;
use std::os::unix::ffi::{OsStrExt, OsStringExt};
use std::os::unix::io::FromRawFd;
use std::path::{Path, PathBuf};
use libc;
use crate::errno::{errno_result, Error, Result};
pub struct TempFile {
path: PathBuf,
file: File,
}
impl TempFile {
pub fn new_with_prefix<P: AsRef<OsStr>>(prefix: P) -> Result<TempFile> {
let mut os_fname = prefix.as_ref().to_os_string();
os_fname.push("XXXXXX");
let raw_fname = CString::new(os_fname.into_vec()).unwrap().into_raw();
let fd = unsafe { libc::mkstemp(raw_fname) };
let c_tempname = unsafe { CString::from_raw(raw_fname) };
let os_tempname = OsStr::from_bytes(c_tempname.as_bytes());
if fd == -1 {
return errno_result();
}
let file = unsafe { File::from_raw_fd(fd) };
Ok(TempFile {
path: PathBuf::from(os_tempname),
file,
})
}
pub fn new_in(path: &Path) -> Result<Self> {
let mut path_buf = path.canonicalize().unwrap();
path_buf.push("");
let temp_file = TempFile::new_with_prefix(path_buf.as_path())?;
Ok(temp_file)
}
pub fn new() -> Result<Self> {
let in_tmp_dir = temp_dir();
let temp_file = TempFile::new_in(in_tmp_dir.as_path())?;
Ok(temp_file)
}
pub fn remove(&mut self) -> Result<()> {
fs::remove_file(&self.path).map_err(Error::from)
}
pub fn as_path(&self) -> &Path {
&self.path
}
pub fn as_file(&self) -> &File {
&self.file
}
}
impl Drop for TempFile {
fn drop(&mut self) {
let _ = self.remove();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
#[test]
fn test_create_file_with_prefix() {
fn between(lower: u8, upper: u8, to_check: u8) -> bool {
(to_check >= lower) && (to_check <= upper)
}
let tempname = "/tmp/asdf";
let t = TempFile::new_with_prefix(tempname).unwrap();
assert_eq!(tempname, "/tmp/asdf");
let path = t.as_path().to_owned();
assert!(path.is_file());
assert!(path.starts_with("/tmp"));
assert_eq!(path.as_os_str().len(), 15);
for n in &path.to_string_lossy().as_bytes()[5..] {
assert!(between(48, 57, *n) || between(65, 90, *n) || between(97, 122, *n));
}
let mut f = t.as_file();
f.write_all(b"hello world").unwrap();
f.sync_all().unwrap();
assert_eq!(f.metadata().unwrap().len(), 11);
}
#[test]
fn test_create_file_new() {
let t = TempFile::new().unwrap();
let path = t.as_path().to_owned();
assert!(path.starts_with("/tmp/"));
}
#[test]
fn test_create_file_new_in() {
let t = TempFile::new_in(Path::new("/tmp")).unwrap();
let path = t.as_path().to_owned();
assert!(path.is_file());
assert!(path.starts_with("/tmp/"));
let t = TempFile::new_in(Path::new("/tmp/")).unwrap();
let path = t.as_path().to_owned();
assert!(path.starts_with("/tmp/"));
}
#[test]
fn test_remove_file() {
let mut t = TempFile::new_with_prefix("/tmp/asdf").unwrap();
let path = t.as_path().to_owned();
assert!(path.starts_with("/tmp"));
assert!(t.remove().is_ok());
assert!(!path.exists());
assert!(t.remove().is_err());
}
#[test]
fn test_drop_file() {
let t = TempFile::new_with_prefix("/tmp/asdf").unwrap();
let path = t.as_path().to_owned();
assert!(path.starts_with("/tmp"));
drop(t);
assert!(!path.exists());
}
}