use std::path::Path;
use std::sync::atomic::{AtomicU64, Ordering};
static NEXT: AtomicU64 = AtomicU64::new(0);
pub fn write(path: &Path, bytes: impl AsRef<[u8]>) -> Result<(), String> {
let parent = path
.parent()
.ok_or_else(|| format!("{} has no parent", path.display()))?;
std::fs::create_dir_all(parent).map_err(|e| format!("{}: {e}", parent.display()))?;
let tmp = parent.join(format!(
".{}.tmp-{}-{}",
path.file_name().and_then(|s| s.to_str()).unwrap_or("state"),
std::process::id(),
NEXT.fetch_add(1, Ordering::Relaxed)
));
std::fs::write(&tmp, bytes).map_err(|e| format!("{}: {e}", tmp.display()))?;
replace(&tmp, path).map_err(|e| format!("{} -> {}: {e}", tmp.display(), path.display()))
}
#[cfg(not(windows))]
fn replace(from: &Path, to: &Path) -> std::io::Result<()> {
std::fs::rename(from, to)
}
#[cfg(windows)]
fn replace(from: &Path, to: &Path) -> std::io::Result<()> {
use std::os::windows::ffi::OsStrExt;
use windows_sys::Win32::Storage::FileSystem::{
MOVEFILE_REPLACE_EXISTING, MOVEFILE_WRITE_THROUGH, MoveFileExW,
};
let from: Vec<u16> = from.as_os_str().encode_wide().chain(Some(0)).collect();
let to: Vec<u16> = to.as_os_str().encode_wide().chain(Some(0)).collect();
let ok = unsafe {
MoveFileExW(
from.as_ptr(),
to.as_ptr(),
MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH,
)
};
if ok == 0 {
Err(std::io::Error::last_os_error())
} else {
Ok(())
}
}