1use fs_err as fs;
26use std::io::{self, Write};
27use std::path::Path;
28
29use fs::OpenOptions;
30
31pub fn safe_write(path: impl AsRef<Path>, content: impl AsRef<[u8]>) -> io::Result<()> {
44 let path = path.as_ref();
45 let content = content.as_ref();
46 let parent = path.parent().unwrap_or_else(|| Path::new("."));
47
48 fs::create_dir_all(parent)?;
50
51 let temp_path = path.with_extension("tmp");
53
54 if temp_path.exists() {
55 fs::remove_file(&temp_path)?;
56 }
57
58 let mut temp_file = OpenOptions::new()
59 .write(true)
60 .create_new(true)
61 .open(&temp_path)?;
62
63 temp_file.write_all(content)?;
65 temp_file.flush()?;
67 temp_file.sync_all()?;
68 drop(temp_file);
70
71 #[cfg(windows)]
72 {
73 if path.exists() {
74 fs::remove_file(path)?;
75 }
76 }
77 fs::rename(&temp_path, path)?;
78 Ok(())
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use tempfile::TempDir;
85
86 #[test]
87 fn test_basic_write() -> io::Result<()> {
88 let temp_dir = TempDir::new()?;
89 let test_path = temp_dir.path().join("test.txt");
90
91 let content = b"Hello, World!";
92 safe_write(&test_path, content)?;
93
94 let read_content = fs::read(&test_path)?;
96 assert_eq!(content, read_content.as_slice());
97
98 Ok(())
99 }
100
101 #[test]
102 fn test_nested_directory_creation() -> io::Result<()> {
103 let temp_dir = TempDir::new()?;
104 let test_path = temp_dir.path().join("nested/dirs/test.txt");
105
106 let content = b"Nested content";
107 safe_write(&test_path, content)?;
108
109 assert!(test_path.exists());
110 let read_content = fs::read(&test_path)?;
111 assert_eq!(content, read_content.as_slice());
112
113 Ok(())
114 }
115
116 #[test]
117 fn test_overwrite_existing() -> io::Result<()> {
118 let temp_dir = TempDir::new()?;
119 let test_path = temp_dir.path().join("overwrite.txt");
120
121 safe_write(&test_path, b"Initial content")?;
123
124 let new_content = b"New content";
126 safe_write(&test_path, new_content)?;
127
128 let read_content = fs::read(&test_path)?;
129 assert_eq!(new_content, read_content.as_slice());
130
131 Ok(())
132 }
133}