use std::path::{Path, PathBuf};
pub(super) fn write_pair_atomically(
dest_path: &Path,
native_path: &Path,
asc_text: &str,
native_toml: &str,
) -> Result<(), String> {
let tmp_asc = temp_sibling(dest_path);
let tmp_native = temp_sibling(native_path);
if let Err(e) = std::fs::write(&tmp_asc, asc_text) {
let _ = std::fs::remove_file(&tmp_asc);
return Err(format!(
"Failed to write {}: {e}. Nothing was saved.",
dest_path.display()
));
}
if let Err(e) = std::fs::write(&tmp_native, native_toml) {
let _ = std::fs::remove_file(&tmp_asc);
let _ = std::fs::remove_file(&tmp_native);
return Err(format!(
"Failed to write {}: {e}. Nothing was saved.",
native_path.display()
));
}
if let Err(message) = backup_existing(dest_path) {
let _ = std::fs::remove_file(&tmp_asc);
let _ = std::fs::remove_file(&tmp_native);
return Err(message);
}
if let Err(message) = backup_existing(native_path) {
let _ = std::fs::remove_file(&tmp_asc);
let _ = std::fs::remove_file(&tmp_native);
return Err(message);
}
if let Err(e) = std::fs::rename(&tmp_asc, dest_path) {
let _ = std::fs::remove_file(&tmp_asc);
let _ = std::fs::remove_file(&tmp_native);
return Err(format!(
"Failed to finalize {}: {e}. Nothing was saved.",
dest_path.display()
));
}
if let Err(e) = std::fs::rename(&tmp_native, native_path) {
return Err(format!(
"Saved '{}' but failed to finalize its native sidecar {}: {e}. The .asc is on \
disk without its native sidecar -- re-save once the problem is fixed.",
dest_path.display(),
native_path.display()
));
}
Ok(())
}
pub(super) fn temp_sibling(path: &Path) -> PathBuf {
let file_name = path.file_name().map_or_else(
|| std::ffi::OsString::from("save.tmp"),
|n| {
let mut s = n.to_os_string();
s.push(".tmp");
s
},
);
path.with_file_name(file_name)
}
fn backup_sibling(path: &Path) -> PathBuf {
let file_name = path.file_name().map_or_else(
|| std::ffi::OsString::from("save.bak"),
|n| {
let mut s = n.to_os_string();
s.push(".bak");
s
},
);
path.with_file_name(file_name)
}
fn backup_existing(path: &Path) -> Result<(), String> {
if !path.is_file() {
return Ok(());
}
let backup = backup_sibling(path);
std::fs::copy(path, &backup).map_err(|e| {
format!(
"Failed to back up {} to {} before overwriting it: {e}. Nothing was saved.",
path.display(),
backup.display()
)
})?;
Ok(())
}