Skip to main content

lean_ctx/
config_io.rs

1use std::path::{Path, PathBuf};
2
3fn backup_path_for(path: &Path) -> Option<PathBuf> {
4    let filename = path.file_name()?.to_string_lossy();
5    Some(path.with_file_name(format!("{filename}.lean-ctx.bak")))
6}
7
8pub fn write_atomic_with_backup(path: &Path, content: &str) -> Result<(), String> {
9    if path.exists() {
10        if let Some(bak) = backup_path_for(path) {
11            let _ = std::fs::copy(path, &bak);
12        }
13    }
14
15    write_atomic(path, content)
16}
17
18pub fn write_atomic(path: &Path, content: &str) -> Result<(), String> {
19    if let Some(parent) = path.parent() {
20        std::fs::create_dir_all(parent).map_err(|e| e.to_string())?;
21    }
22
23    let parent = path
24        .parent()
25        .ok_or_else(|| "invalid path (no parent directory)".to_string())?;
26    let filename = path
27        .file_name()
28        .ok_or_else(|| "invalid path (no filename)".to_string())?
29        .to_string_lossy();
30
31    let pid = std::process::id();
32    let nanos = std::time::SystemTime::now()
33        .duration_since(std::time::UNIX_EPOCH)
34        .map(|d| d.as_nanos())
35        .unwrap_or(0);
36
37    let tmp = parent.join(format!(".{filename}.lean-ctx.tmp.{pid}.{nanos}"));
38    std::fs::write(&tmp, content).map_err(|e| e.to_string())?;
39
40    #[cfg(windows)]
41    {
42        if path.exists() {
43            let _ = std::fs::remove_file(path);
44        }
45    }
46
47    std::fs::rename(&tmp, path).map_err(|e| {
48        format!(
49            "atomic write failed: {} (tmp: {})",
50            e,
51            tmp.to_string_lossy()
52        )
53    })?;
54
55    Ok(())
56}