use std::fs::{self, File};
use std::io;
use std::path::{Path, PathBuf};
pub(crate) fn atomic_write<F>(path: &Path, write: F) -> io::Result<()>
where
F: FnOnce(&mut File) -> io::Result<()>,
{
let mut tmp_os = path.as_os_str().to_owned();
tmp_os.push(".tmp");
let tmp_path = PathBuf::from(tmp_os);
let result = (|| -> io::Result<()> {
let mut file = File::create(&tmp_path)?;
write(&mut file)?;
file.sync_all()?;
fs::rename(&tmp_path, path)?;
fsync_parent_best_effort(path)
})();
if result.is_err() {
let _ = fs::remove_file(&tmp_path);
}
result
}
#[cfg(unix)]
fn fsync_parent_best_effort(path: &Path) -> io::Result<()> {
let parent = path
.parent()
.filter(|p| !p.as_os_str().is_empty())
.unwrap_or(Path::new("."));
match File::open(parent).and_then(|f| f.sync_all()) {
Ok(()) => Ok(()),
Err(e) if is_dir_fsync_unsupported(&e) => Ok(()),
Err(e) => Err(e),
}
}
#[cfg(not(unix))]
fn fsync_parent_best_effort(_path: &Path) -> io::Result<()> {
Ok(())
}
#[cfg(unix)]
fn is_dir_fsync_unsupported(e: &io::Error) -> bool {
if e.kind() == io::ErrorKind::Unsupported {
return true;
}
cfg!(target_os = "macos") && e.raw_os_error() == Some(45)
}