use std::fs::{self, File};
use std::io::Write;
use std::path::{Path, PathBuf};
use prikk_error::{PrikkError, Result};
pub(crate) fn write_file_atomically(path: &Path, bytes: &[u8]) -> Result<()> {
let Some(parent) = path.parent() else {
return Err(PrikkError::Io(
"atomic write path has no parent directory".to_string(),
));
};
fs::create_dir_all(parent)?;
let tmp_path = temporary_path(path);
{
let mut file = File::create(&tmp_path)?;
file.write_all(bytes)?;
file.sync_all()?;
}
fs::rename(&tmp_path, path)?;
sync_directory_best_effort(parent)?;
Ok(())
}
pub(crate) fn temporary_path(path: &Path) -> PathBuf {
let mut file_name = path
.file_name()
.map(|name| name.to_os_string())
.unwrap_or_default();
file_name.push(format!(".tmp.{}", std::process::id()));
path.with_file_name(file_name)
}
pub(crate) fn sync_directory_best_effort(path: &Path) -> Result<()> {
match File::open(path) {
Ok(file) => {
let _ = file.sync_all();
Ok(())
}
Err(err) if err.kind() == std::io::ErrorKind::PermissionDenied => Ok(()),
Err(err) => Err(err.into()),
}
}
pub(crate) fn len_to_u16(len: usize) -> Result<u16> {
u16::try_from(len).map_err(|_| PrikkError::MalformedData("length exceeds u16".to_string()))
}
pub(crate) fn len_to_u32(len: usize) -> Result<u32> {
u32::try_from(len).map_err(|_| PrikkError::MalformedData("length exceeds u32".to_string()))
}
pub(crate) fn len_to_u64(len: usize) -> Result<u64> {
u64::try_from(len).map_err(|_| PrikkError::MalformedData("length exceeds u64".to_string()))
}