chore: cleanups and refactorings for cleaner code

This commit is contained in:
2026-07-18 21:39:16 +02:00
parent b438a99999
commit e5080b7282
63 changed files with 3454 additions and 4093 deletions

View File

@@ -1,6 +1,10 @@
use std::ffi::OsString;
use std::fs;
use std::io::{self, Write};
use std::path::Path;
use std::sync::atomic::{AtomicU64, Ordering};
static NEXT_TEMP_ID: AtomicU64 = AtomicU64::new(1);
/// Write `content` to `path` atomically: write to a temp file in the same
/// directory, then rename. Creates parent directories if missing.
@@ -8,12 +12,36 @@ pub fn atomic_write(path: &Path, content: &[u8]) -> io::Result<()> {
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)?;
}
let tmp_path = path.with_extension("tmp");
let mut file = fs::File::create(&tmp_path)?;
file.write_all(content)?;
file.sync_all()?;
fs::rename(&tmp_path, path)?;
Ok(())
let parent = path.parent().unwrap_or_else(|| Path::new("."));
let file_name = path
.file_name()
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "path has no file name"))?;
loop {
let mut temp_name = OsString::from(".");
temp_name.push(file_name);
temp_name.push(format!(
".{}.{}.tmp",
std::process::id(),
NEXT_TEMP_ID.fetch_add(1, Ordering::Relaxed)
));
let tmp_path = parent.join(temp_name);
let mut file = match fs::File::create_new(&tmp_path) {
Ok(file) => file,
Err(error) if error.kind() == io::ErrorKind::AlreadyExists => continue,
Err(error) => return Err(error),
};
let result = (|| {
file.write_all(content)?;
file.sync_all()?;
drop(file);
fs::rename(&tmp_path, path)
})();
if result.is_err() {
let _ = fs::remove_file(&tmp_path);
}
return result;
}
}
/// Convenience wrapper for UTF-8 string content.
@@ -50,4 +78,27 @@ mod tests {
atomic_write_str(&path, "v2").unwrap();
assert_eq!(fs::read_to_string(&path).unwrap(), "v2");
}
#[test]
fn concurrent_sibling_writes_do_not_share_a_temp_file() {
let dir = TempDir::new().unwrap();
let markdown = dir.path().join("a.en.md");
let metadata = dir.path().join("a.en.meta");
let barrier = std::sync::Arc::new(std::sync::Barrier::new(2));
let write = |path: std::path::PathBuf, content: &'static str| {
let barrier = barrier.clone();
std::thread::spawn(move || {
barrier.wait();
atomic_write_str(&path, content)
})
};
let first = write(markdown.clone(), "markdown");
let second = write(metadata.clone(), "metadata");
first.join().unwrap().unwrap();
second.join().unwrap().unwrap();
assert_eq!(fs::read_to_string(markdown).unwrap(), "markdown");
assert_eq!(fs::read_to_string(metadata).unwrap(), "metadata");
}
}