1use anyhow::Result;
4use std::fs;
5use std::path::Path;
6
7pub fn atomic_write(file_path: &str, content: &str) -> Result<()> {
17 let path = Path::new(file_path);
18 let parent = path.parent().unwrap_or_else(|| Path::new("."));
19 let file_name = path.file_name().unwrap_or_default().to_string_lossy();
20 let temp_path = parent.join(format!(".{file_name}.tmp"));
21
22 fs::write(&temp_path, content)?;
24
25 fs::rename(&temp_path, file_path)?;
27
28 Ok(())
29}
30
31#[cfg(test)]
32#[allow(clippy::unwrap_used)]
33mod tests {
34 use super::*;
35 use tempfile::TempDir;
36
37 #[test]
38 fn test_atomic_write_creates_file() {
39 let temp_dir = TempDir::new().unwrap();
40 let file_path = temp_dir.path().join("test.txt");
41 let file_path_str = file_path.to_str().unwrap();
42
43 atomic_write(file_path_str, "Hello, World!").unwrap();
44
45 let content = fs::read_to_string(&file_path).unwrap();
46 assert_eq!(content, "Hello, World!");
47 }
48
49 #[test]
50 fn test_atomic_write_overwrites_existing() {
51 let temp_dir = TempDir::new().unwrap();
52 let file_path = temp_dir.path().join("test.txt");
53 let file_path_str = file_path.to_str().unwrap();
54
55 fs::write(&file_path, "Original content").unwrap();
56 atomic_write(file_path_str, "New content").unwrap();
57
58 let content = fs::read_to_string(&file_path).unwrap();
59 assert_eq!(content, "New content");
60 }
61
62 #[test]
63 fn test_atomic_write_no_temp_file_remains() {
64 let temp_dir = TempDir::new().unwrap();
65 let file_path = temp_dir.path().join("test.txt");
66 let file_path_str = file_path.to_str().unwrap();
67
68 atomic_write(file_path_str, "content").unwrap();
69
70 let temp_path = temp_dir.path().join(".test.txt.tmp");
72 assert!(!temp_path.exists());
73 }
74
75 #[test]
76 fn test_atomic_write_unicode_content() {
77 let temp_dir = TempDir::new().unwrap();
78 let file_path = temp_dir.path().join("test.txt");
79 let file_path_str = file_path.to_str().unwrap();
80
81 let content = "こんにちは世界!🌍";
82 atomic_write(file_path_str, content).unwrap();
83
84 let read_content = fs::read_to_string(&file_path).unwrap();
85 assert_eq!(read_content, content);
86 }
87}