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 mut tmp_name = path
15 .file_name()
16 .unwrap_or_default()
17 .to_os_string();
18 tmp_name.push(format!(".purple_tmp.{}", std::process::id()));
19 let tmp_path = path.with_file_name(tmp_name);
20
21 #[cfg(unix)]
22 {
23 use std::io::Write;
24 use std::os::unix::fs::OpenOptionsExt;
25 let open = || {
28 fs::OpenOptions::new()
29 .write(true)
30 .create_new(true)
31 .mode(0o600)
32 .open(&tmp_path)
33 };
34 let mut file = match open() {
35 Ok(f) => f,
36 Err(e) if e.kind() == io::ErrorKind::AlreadyExists => {
37 let _ = fs::remove_file(&tmp_path);
38 open().map_err(|e| {
39 io::Error::new(
40 e.kind(),
41 format!("Failed to create temp file {}: {}", tmp_path.display(), e),
42 )
43 })?
44 }
45 Err(e) => {
46 return Err(io::Error::new(
47 e.kind(),
48 format!("Failed to create temp file {}: {}", tmp_path.display(), e),
49 ));
50 }
51 };
52 file.write_all(content)?;
53 }
54
55 #[cfg(not(unix))]
56 fs::write(&tmp_path, content)?;
57
58 let result = fs::rename(&tmp_path, path);
59 if result.is_err() {
60 let _ = fs::remove_file(&tmp_path);
61 }
62 result
63}