feat: [#1932] l-s v0.5.1 原子写入保护及文档更新
This commit is contained in:
+44
-6
@@ -4,10 +4,10 @@ mod head_hash;
|
||||
mod meta;
|
||||
mod utils;
|
||||
|
||||
use std::fs::{self, File};
|
||||
use std::fs::{self, File, OpenOptions};
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::time::Instant;
|
||||
use std::time::{Instant, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use anyhow::{Context, Result};
|
||||
use clap::Parser;
|
||||
@@ -57,7 +57,7 @@ fn process_file(path: &Path) -> Result<()> {
|
||||
tracker.finish("处理完成");
|
||||
let json = meta.to_pretty_json()?;
|
||||
println!("{}", json);
|
||||
fs::write(&save_path, json)?;
|
||||
write_atomic(&save_path, &json)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -98,9 +98,7 @@ fn process_dir(path: &Path) -> Result<()> {
|
||||
if !meta_path.exists() {
|
||||
let snapshot = DirSnapshot::build_root(path)?;
|
||||
let json = serde_json::to_string_pretty(&snapshot)?;
|
||||
let mut file = File::create(&meta_path)
|
||||
.with_context(|| format!("无法写入: {}", meta_path.display()))?;
|
||||
file.write_all(json.as_bytes())?;
|
||||
write_atomic(&meta_path, &json)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
@@ -151,3 +149,43 @@ fn process_dir(path: &Path) -> Result<()> {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_atomic(path: &Path, contents: &str) -> Result<()> {
|
||||
let parent = path
|
||||
.parent()
|
||||
.map(Path::to_path_buf)
|
||||
.unwrap_or_else(|| PathBuf::from("."));
|
||||
let nanos = SystemTime::now()
|
||||
.duration_since(UNIX_EPOCH)
|
||||
.map(|duration| duration.as_nanos())
|
||||
.unwrap_or(0);
|
||||
let tmp_path = parent.join(format!(".l-s-tmp-{}-{nanos}", std::process::id()));
|
||||
|
||||
let result = (|| -> Result<()> {
|
||||
let mut file = OpenOptions::new()
|
||||
.write(true)
|
||||
.create_new(true)
|
||||
.open(&tmp_path)
|
||||
.with_context(|| format!("无法创建临时文件: {}", tmp_path.display()))?;
|
||||
file.write_all(contents.as_bytes())
|
||||
.with_context(|| format!("无法写入临时文件: {}", tmp_path.display()))?;
|
||||
file.sync_all()
|
||||
.with_context(|| format!("无法同步临时文件: {}", tmp_path.display()))?;
|
||||
drop(file);
|
||||
|
||||
fs::rename(&tmp_path, path).with_context(|| {
|
||||
format!(
|
||||
"无法将临时文件重命名为目标文件: {} -> {}",
|
||||
tmp_path.display(),
|
||||
path.display()
|
||||
)
|
||||
})?;
|
||||
Ok(())
|
||||
})();
|
||||
|
||||
if result.is_err() {
|
||||
let _ = fs::remove_file(&tmp_path);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user