Files
cdxs/src/atomic.rs
T

54 lines
1.7 KiB
Rust

use std::fs;
use std::path::{Path, PathBuf};
use anyhow::{Context, Result};
use chrono::Utc;
/// Write a file through a sibling temporary file and then rename it into place.
/// This keeps auth/config files from being left half-written after failures.
pub fn write_atomic(path: &Path, content: &str) -> Result<()> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.with_context(|| format!("创建目录失败: {}", parent.display()))?;
}
let tmp = temp_path(path);
fs::write(&tmp, content).with_context(|| format!("写入临时文件失败: {}", tmp.display()))?;
fs::rename(&tmp, path).with_context(|| {
format!(
"替换目标文件失败: tmp={}, target={}",
tmp.display(),
path.display()
)
})?;
Ok(())
}
/// Backup an existing file into `<codex_home>/cdxs-backups/`.
pub fn backup_if_exists(path: &Path, codex_home: &Path, label: &str) -> Result<Option<PathBuf>> {
if !path.exists() {
return Ok(None);
}
let backup_dir = codex_home.join("cdxs-backups");
fs::create_dir_all(&backup_dir)
.with_context(|| format!("创建备份目录失败: {}", backup_dir.display()))?;
let stamp = Utc::now().format("%Y%m%d-%H%M%S%.3f");
let backup = backup_dir.join(format!("{label}-{stamp}.bak"));
fs::copy(path, &backup).with_context(|| {
format!(
"备份文件失败: source={}, backup={}",
path.display(),
backup.display()
)
})?;
Ok(Some(backup))
}
fn temp_path(path: &Path) -> PathBuf {
let file_name = path
.file_name()
.and_then(|value| value.to_str())
.unwrap_or("cdxs.tmp");
path.with_file_name(format!(".{file_name}.tmp"))
}