use std::io::Write;
use std::path::Path;
pub fn write_atomic(path: &str, data: &[u8], mode: u32) -> anyhow::Result<()> {
let target = Path::new(path);
let parent = target
.parent()
.ok_or_else(|| anyhow::anyhow!("path has no parent directory: {path}"))?;
std::fs::create_dir_all(parent)?;
let mut tmp = tempfile::NamedTempFile::new_in(parent)?;
tmp.write_all(data)?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
tmp.as_file_mut()
.set_permissions(std::fs::Permissions::from_mode(mode))?;
}
tmp.persist(target)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
#[test]
fn test_write_atomic_creates_file() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("test.txt");
let path_str = path.to_string_lossy().to_string();
write_atomic(&path_str, b"hello", 0o644).unwrap();
let content = fs::read_to_string(&path).unwrap();
assert_eq!(content, "hello");
}
#[test]
fn test_write_atomic_overwrites() {
let dir = tempfile::tempdir().unwrap();
let path = dir.path().join("test.txt");
let path_str = path.to_string_lossy().to_string();
write_atomic(&path_str, b"first", 0o644).unwrap();
write_atomic(&path_str, b"second", 0o644).unwrap();
let content = fs::read_to_string(&path).unwrap();
assert_eq!(content, "second");
}
}