coding_tools/
atomicfile.rs1use std::path::Path;
8use std::sync::atomic::{AtomicU64, Ordering};
9
10static NEXT: AtomicU64 = AtomicU64::new(0);
11
12pub fn write(path: &Path, bytes: impl AsRef<[u8]>) -> Result<(), String> {
13 let parent = path
14 .parent()
15 .ok_or_else(|| format!("{} has no parent", path.display()))?;
16 std::fs::create_dir_all(parent).map_err(|e| format!("{}: {e}", parent.display()))?;
17 let tmp = parent.join(format!(
18 ".{}.tmp-{}-{}",
19 path.file_name().and_then(|s| s.to_str()).unwrap_or("state"),
20 std::process::id(),
21 NEXT.fetch_add(1, Ordering::Relaxed)
22 ));
23 std::fs::write(&tmp, bytes).map_err(|e| format!("{}: {e}", tmp.display()))?;
24 replace(&tmp, path).map_err(|e| format!("{} -> {}: {e}", tmp.display(), path.display()))
25}
26
27#[cfg(not(windows))]
28fn replace(from: &Path, to: &Path) -> std::io::Result<()> {
29 std::fs::rename(from, to)
30}
31
32#[cfg(windows)]
33fn replace(from: &Path, to: &Path) -> std::io::Result<()> {
34 use std::os::windows::ffi::OsStrExt;
35 use windows_sys::Win32::Storage::FileSystem::{
36 MOVEFILE_REPLACE_EXISTING, MOVEFILE_WRITE_THROUGH, MoveFileExW,
37 };
38
39 let from: Vec<u16> = from.as_os_str().encode_wide().chain(Some(0)).collect();
40 let to: Vec<u16> = to.as_os_str().encode_wide().chain(Some(0)).collect();
41 let ok = unsafe {
44 MoveFileExW(
45 from.as_ptr(),
46 to.as_ptr(),
47 MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH,
48 )
49 };
50 if ok == 0 {
51 Err(std::io::Error::last_os_error())
52 } else {
53 Ok(())
54 }
55}