use std::env::temp_dir;
use std::ffi::OsStr;
use std::fs;
use std::fs::{File, OpenOptions};
use std::path::{Path, PathBuf};
use crate::errno::{Error, Result};
use crate::rand::rand_alphanumerics;
pub struct TempFile {
path: PathBuf,
file: Option<File>,
}
impl TempFile {
pub fn new_with_prefix<P: AsRef<OsStr>>(prefix: P) -> Result<TempFile> {
let file_path_str = format!(
"{}{}",
prefix.as_ref().to_str().unwrap_or_default(),
rand_alphanumerics(6).to_str().unwrap_or_default()
);
let file_path_buf = PathBuf::from(&file_path_str);
let file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open(file_path_buf.as_path())?;
Ok(TempFile {
path: file_path_buf,
file: Some(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.as_ref().unwrap()
}
pub fn into_file(mut self) -> File {
self.file.take().unwrap()
}
}
impl Drop for TempFile {
fn drop(&mut self) {
let _ = self.remove();
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::{Read, 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 mut prefix = temp_dir();
prefix.push("asdf");
let t = TempFile::new_with_prefix(&prefix).unwrap();
let path = t.as_path().to_owned();
assert!(path.is_file());
assert!(path.starts_with(temp_dir()));
assert_eq!(path.file_name().unwrap().to_string_lossy().len(), 10);
for n in path.file_name().unwrap().to_string_lossy().bytes() {
assert!(
between('0' as u8, '9' as u8, n)
|| between('a' as u8, 'z' as u8, n)
|| between('A' as u8, 'Z' as u8, 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(temp_dir().canonicalize().unwrap()));
}
#[test]
fn test_create_file_new_in() {
let t = TempFile::new_in(temp_dir().as_path()).unwrap();
let path = t.as_path().to_owned();
assert!(path.is_file());
assert!(path.starts_with(temp_dir().canonicalize().unwrap()));
let t = TempFile::new_in(temp_dir().as_path()).unwrap();
let path = t.as_path().to_owned();
assert!(path.starts_with(temp_dir().canonicalize().unwrap()));
}
#[test]
fn test_remove_file() {
let mut prefix = temp_dir();
prefix.push("asdf");
let mut t = TempFile::new_with_prefix(prefix).unwrap();
let path = t.as_path().to_owned();
assert!(t.remove().is_ok());
assert!(!path.exists());
assert!(t.remove().is_err());
}
#[test]
fn test_drop_file() {
let mut prefix = temp_dir();
prefix.push("asdf");
let t = TempFile::new_with_prefix(prefix).unwrap();
let path = t.as_path().to_owned();
assert!(path.starts_with(temp_dir()));
drop(t);
assert!(!path.exists());
}
#[test]
fn test_into_file() {
let mut prefix = temp_dir();
prefix.push("asdf");
let text = b"hello world";
let temp_file = TempFile::new_with_prefix(prefix).unwrap();
let path = temp_file.as_path().to_owned();
fs::write(path, text).unwrap();
let mut file = temp_file.into_file();
let mut buf: Vec<u8> = Vec::new();
file.read_to_end(&mut buf).unwrap();
assert_eq!(buf, text);
}
}