1use std::fs::{File, OpenOptions};
4use std::io::Write;
5use std::path::Path;
6
7use anyhow::{Context, Result};
8use fs2::FileExt;
9use tempfile::NamedTempFile;
10
11pub fn atomic_write(path: &Path, bytes: &[u8]) -> Result<()> {
12 let parent = path
13 .parent()
14 .filter(|p| !p.as_os_str().is_empty())
15 .unwrap_or_else(|| Path::new("."));
16 std::fs::create_dir_all(parent)?;
17 let mut temp = NamedTempFile::new_in(parent)?;
18 if let Ok(metadata) = std::fs::metadata(path) {
19 temp.as_file().set_permissions(metadata.permissions())?;
20 }
21 temp.write_all(bytes)?;
22 temp.as_file().sync_all()?;
23 temp.persist(path)
24 .with_context(|| format!("replacing {}", path.display()))?;
25 #[cfg(unix)]
26 File::open(parent)?.sync_all()?;
27 Ok(())
28}
29
30pub fn lock(path: &Path) -> Result<File> {
31 if let Some(parent) = path.parent() {
32 std::fs::create_dir_all(parent)?;
33 }
34 let file = OpenOptions::new()
35 .create(true)
36 .truncate(false)
37 .write(true)
38 .open(path)?;
39 file.lock_exclusive()?;
40 Ok(file)
41}