use crate::APP_NAME;
use std::{
fs::File,
io::{self, Write},
path::{Path, PathBuf},
};
#[cfg(unix)]
const OUTPUT_FILE_MODE: u32 = 0o600;
fn create_output_file(prefix: &str, extension: &str) -> io::Result<(File, PathBuf)> {
let file = tempfile::Builder::new()
.prefix(&format!("{APP_NAME}-{prefix}-"))
.suffix(extension)
.tempfile_in(std::env::temp_dir())?;
let (file, path) = file.keep()?;
set_owner_only_permissions(&path);
Ok((file, path))
}
fn set_owner_only_permissions(path: &Path) {
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
_ = std::fs::set_permissions(path, std::fs::Permissions::from_mode(OUTPUT_FILE_MODE));
}
#[cfg(not(unix))]
{
_ = path;
}
}
pub fn write_output_file(prefix: &str, extension: &str, data: &[u8]) -> io::Result<PathBuf> {
let (mut file, path) = create_output_file(prefix, extension)?;
file.write_all(data)?;
Ok(path)
}
pub fn create_output_file_stream(prefix: &str, extension: &str) -> io::Result<(File, PathBuf)> {
create_output_file(prefix, extension)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn writes_data_to_a_temp_file() {
let path = write_output_file("prux-test", ".txt", b"hello output").unwrap();
assert_eq!(std::fs::read(&path).unwrap(), b"hello output");
assert!(
path.starts_with(std::env::temp_dir()),
"应在 OS 临时目录下: {path:?}"
);
assert!(
path.file_name()
.unwrap()
.to_string_lossy()
.starts_with(&format!("{}-prux-test-", crate::APP_NAME)),
"文件名应带前缀: {path:?}"
);
let _ = std::fs::remove_file(&path);
}
#[test]
fn creates_a_new_path_each_time() {
let a = write_output_file("prux-test", ".txt", b"a").unwrap();
let b = write_output_file("prux-test", ".txt", b"b").unwrap();
assert_ne!(a, b);
let _ = std::fs::remove_file(&a);
let _ = std::fs::remove_file(&b);
}
#[test]
fn streamed_file_survives_the_handle() {
let path = {
let (mut file, path) = create_output_file_stream("prux-test", ".log").unwrap();
file.write_all(b"streamed").unwrap();
path
};
assert_eq!(std::fs::read(&path).unwrap(), b"streamed");
let _ = std::fs::remove_file(&path);
}
#[cfg(unix)]
#[test]
fn output_file_is_owner_only() {
use std::os::unix::fs::PermissionsExt;
let path = write_output_file("prux-test", ".txt", b"secret").unwrap();
let mode = std::fs::metadata(&path).unwrap().permissions().mode();
assert_eq!(mode & 0o777, OUTPUT_FILE_MODE, "权限应为 0o600: {mode:o}");
let _ = std::fs::remove_file(&path);
}
}