agent_teams/util/
atomic_write.rs1use std::path::Path;
7
8use crate::error::{Error, Result};
9
10pub fn atomic_write(path: &Path, data: &[u8]) -> Result<()> {
16 let dir = path.parent().ok_or_else(|| Error::AtomicWriteFailed {
17 path: path.to_path_buf(),
18 reason: "no parent directory".into(),
19 })?;
20
21 std::fs::create_dir_all(dir)?;
23
24 let mut tmp = tempfile::NamedTempFile::new_in(dir).map_err(|e| Error::AtomicWriteFailed {
25 path: path.to_path_buf(),
26 reason: e.to_string(),
27 })?;
28
29 std::io::Write::write_all(&mut tmp, data).map_err(|e| Error::AtomicWriteFailed {
30 path: path.to_path_buf(),
31 reason: e.to_string(),
32 })?;
33
34 tmp.persist(path).map_err(|e| Error::AtomicWriteFailed {
35 path: path.to_path_buf(),
36 reason: e.to_string(),
37 })?;
38
39 Ok(())
40}
41
42pub fn atomic_write_json<T: serde::Serialize>(path: &Path, value: &T) -> Result<()> {
44 let data = serde_json::to_vec_pretty(value)?;
45 atomic_write(path, &data)
46}
47
48#[cfg(test)]
49mod tests {
50 use super::*;
51
52 #[test]
53 fn atomic_write_creates_file() {
54 let dir = tempfile::tempdir().unwrap();
55 let path = dir.path().join("test.json");
56
57 atomic_write(&path, b"hello world").unwrap();
58 assert_eq!(std::fs::read_to_string(&path).unwrap(), "hello world");
59 }
60
61 #[test]
62 fn atomic_write_json_round_trip() {
63 let dir = tempfile::tempdir().unwrap();
64 let path = dir.path().join("data.json");
65
66 let data = serde_json::json!({"key": "value", "num": 42});
67 atomic_write_json(&path, &data).unwrap();
68
69 let read: serde_json::Value =
70 serde_json::from_str(&std::fs::read_to_string(&path).unwrap()).unwrap();
71 assert_eq!(read["key"], "value");
72 assert_eq!(read["num"], 42);
73 }
74}