use std::env::temp_dir;
use std::ffi::{CString, OsStr, OsString};
use std::fs;
use std::os::unix::ffi::OsStringExt;
use std::path::{Path, PathBuf};
use libc;
use crate::errno::{errno_result, Error, Result};
pub struct TempDir {
path: PathBuf,
}
impl TempDir {
pub fn new_with_prefix<P: AsRef<OsStr>>(prefix: P) -> Result<TempDir> {
let mut dir_string = prefix.as_ref().to_os_string();
dir_string.push("XXXXXX");
let dir_name = CString::new(dir_string.into_vec()).unwrap();
let mut dir_bytes = dir_name.into_bytes_with_nul();
let ret = unsafe {
libc::mkdtemp(dir_bytes.as_mut_ptr() as *mut libc::c_char)
};
if ret.is_null() {
return errno_result();
}
dir_bytes.pop(); Ok(TempDir {
path: PathBuf::from(OsString::from_vec(dir_bytes)),
})
}
pub fn new_in(path: &Path) -> Result<TempDir> {
let mut path_buf = path.canonicalize().unwrap();
path_buf.push("");
let temp_dir = TempDir::new_with_prefix(path_buf)?;
Ok(temp_dir)
}
pub fn new() -> Result<TempDir> {
let mut in_tmp_dir = temp_dir();
in_tmp_dir.push("");
let temp_dir = TempDir::new_in(in_tmp_dir.as_path())?;
Ok(temp_dir)
}
pub fn remove(&self) -> Result<()> {
fs::remove_dir_all(&self.path).map_err(Error::from)
}
pub fn as_path(&self) -> &Path {
self.path.as_ref()
}
}
impl Drop for TempDir {
fn drop(&mut self) {
let _ = self.remove();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_dir() {
let t = TempDir::new().unwrap();
let path = t.as_path();
assert!(path.exists());
assert!(path.is_dir());
assert!(path.starts_with("/tmp/"));
}
#[test]
fn test_create_dir_with_prefix() {
let t = TempDir::new_with_prefix("/tmp/testdir").unwrap();
let path = t.as_path();
assert!(path.exists());
assert!(path.is_dir());
assert!(path.to_str().unwrap().contains("/tmp/testdir"));
}
#[test]
fn test_remove_dir() {
let t = TempDir::new().unwrap();
let path = t.as_path().to_owned();
assert!(t.remove().is_ok());
assert!(t.remove().is_err());
assert!(!path.exists());
}
#[test]
fn test_create_dir_in() {
let t = TempDir::new_in(Path::new("/tmp")).unwrap();
let path = t.as_path();
assert!(path.exists());
assert!(path.is_dir());
assert!(path.starts_with("/tmp/"));
let t = TempDir::new_in(Path::new("/tmp")).unwrap();
let path = t.as_path();
assert!(path.exists());
assert!(path.is_dir());
assert!(path.starts_with("/tmp"));
}
#[test]
fn test_drop() {
use std::mem::drop;
let t = TempDir::new_with_prefix("/tmp/asdf").unwrap();
let path = t.as_path().to_owned();
drop(t);
assert!(!(path.exists()));
}
}