1use std::fs;
2use std::io;
3use std::path::Path;
4
5pub fn atomic_write(path: &Path, content: &[u8]) -> io::Result<()> {
9 if let Some(parent) = path.parent() {
11 fs::create_dir_all(parent)?;
12 }
13
14 let tmp_path = path.with_extension(format!("purple_tmp.{}", std::process::id()));
15
16 #[cfg(unix)]
17 {
18 use std::io::Write;
19 use std::os::unix::fs::OpenOptionsExt;
20 let open = || {
23 fs::OpenOptions::new()
24 .write(true)
25 .create_new(true)
26 .mode(0o600)
27 .open(&tmp_path)
28 };
29 let mut file = match open() {
30 Ok(f) => f,
31 Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
32 let _ = fs::remove_file(&tmp_path);
33 open().map_err(|e| {
34 io::Error::new(
35 e.kind(),
36 format!("Failed to create temp file {}: {}", tmp_path.display(), e),
37 )
38 })?
39 }
40 Err(e) => {
41 return Err(io::Error::new(
42 e.kind(),
43 format!("Failed to create temp file {}: {}", tmp_path.display(), e),
44 ));
45 }
46 };
47 file.write_all(content)?;
48 }
49
50 #[cfg(not(unix))]
51 fs::write(&tmp_path, content)?;
52
53 let result = fs::rename(&tmp_path, path);
54 if result.is_err() {
55 let _ = fs::remove_file(&tmp_path);
56 }
57 result
58}