use std::fs::{self, File};
use std::io;
use std::ops::Deref;
use std::path::{Path, PathBuf};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum TempError {
#[error("Failed to create temporary file: {0}")]
CreateFileFailed(#[from] io::Error),
}
#[derive(Debug)]
pub(crate) enum TempGuard {
File(PathBuf),
}
impl TempGuard {
pub(crate) fn path(&self) -> &Path {
match self {
TempGuard::File(path) => path,
}
}
}
impl Drop for TempGuard {
fn drop(&mut self) {
match self {
TempGuard::File(path) => {
let _ = fs::remove_file(path);
}
}
}
}
impl Deref for TempGuard {
type Target = Path;
fn deref(&self) -> &Self::Target {
self.path()
}
}
pub(crate) fn create_temp_file(prefix: &str, extension: &str) -> Result<TempGuard, TempError> {
let temp_dir = std::env::temp_dir();
let ulid = ulid::Ulid::new();
let filename = format!("{}_{}.{}", prefix, ulid, extension);
let path = temp_dir.join(filename);
File::create(&path)?;
Ok(TempGuard::File(path))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_create_temp_file() {
let temp = create_temp_file("test", "txt").unwrap();
let path = temp.path().to_path_buf();
assert!(path.exists());
assert!(path.is_file());
let filename = path.file_name().unwrap().to_str().unwrap();
assert!(filename.starts_with("test_"));
assert!(filename.ends_with(".txt"));
drop(temp);
assert!(!path.exists());
}
#[test]
fn test_temp_guard_path() {
let temp = create_temp_file("test", "dat").unwrap();
let path = temp.path();
assert!(path.exists());
assert!(path.is_absolute());
}
#[test]
fn test_multiple_temp_files_unique() {
let temp1 = create_temp_file("test", "txt").unwrap();
let temp2 = create_temp_file("test", "txt").unwrap();
assert_ne!(temp1.path(), temp2.path());
assert!(temp1.path().exists());
assert!(temp2.path().exists());
}
#[test]
fn test_temp_file_cleanup_on_drop() {
let path = {
let temp = create_temp_file("cleanup_test", "tmp").unwrap();
let path = temp.path().to_path_buf();
assert!(path.exists());
path
};
assert!(!path.exists());
}
}