#![allow(dead_code)]
use std::fs;
use std::path::Path;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EditorSavePattern {
DirectWrite,
VimAtomicRename,
JetBrainsAtomicSave,
VscodeSafeSave,
EmacsBackup,
}
impl EditorSavePattern {
#[must_use]
pub fn all() -> &'static [Self] {
&[
Self::DirectWrite,
Self::VimAtomicRename,
Self::JetBrainsAtomicSave,
Self::VscodeSafeSave,
Self::EmacsBackup,
]
}
}
pub fn simulate_save(path: &Path, content: &[u8], pattern: EditorSavePattern) {
match pattern {
EditorSavePattern::DirectWrite => {
fs::write(path, content).expect("DirectWrite failed");
}
EditorSavePattern::VimAtomicRename => {
let file_name = path.file_name().expect("path must have filename");
let swp_name = format!(".{}.swp", file_name.to_string_lossy());
let swp_path = path.with_file_name(&swp_name);
fs::write(&swp_path, content).expect("Vim: write swap failed");
std::thread::sleep(std::time::Duration::from_millis(5));
fs::rename(&swp_path, path).expect("Vim: rename swap → target failed");
}
EditorSavePattern::JetBrainsAtomicSave => {
let tmp_path = path.with_extension(format!(
"{}___jb_tmp___",
path.extension()
.map(|e| format!("{}.", e.to_string_lossy()))
.unwrap_or_default()
));
let old_path = path.with_extension(format!(
"{}___jb_old___",
path.extension()
.map(|e| format!("{}.", e.to_string_lossy()))
.unwrap_or_default()
));
if path.exists() {
fs::rename(path, &old_path).expect("JetBrains: rename target → old failed");
}
fs::write(&tmp_path, content).expect("JetBrains: write tmp failed");
{
let f = fs::File::open(&tmp_path).expect("JetBrains: open tmp for sync failed");
f.sync_all().expect("JetBrains: fsync failed");
}
std::thread::sleep(std::time::Duration::from_millis(5));
fs::rename(&tmp_path, path).expect("JetBrains: rename tmp → target failed");
if old_path.exists() {
let _ = fs::remove_file(&old_path);
}
}
EditorSavePattern::VscodeSafeSave => {
let bak_path = path.with_extension(format!(
"{}.bak",
path.extension()
.map(|e| e.to_string_lossy().to_string())
.unwrap_or_default()
));
if path.exists() {
fs::rename(path, &bak_path).expect("VSCode: rename target → bak failed");
}
std::thread::sleep(std::time::Duration::from_millis(5));
fs::write(path, content).expect("VSCode: write new content failed");
std::thread::sleep(std::time::Duration::from_millis(5));
if bak_path.exists() {
let _ = fs::remove_file(&bak_path);
}
}
EditorSavePattern::EmacsBackup => {
let backup_path = path.with_extension(format!(
"{}~",
path.extension()
.map(|e| e.to_string_lossy().to_string())
.unwrap_or_default()
));
if path.exists() {
fs::copy(path, &backup_path).expect("Emacs: copy to backup failed");
}
std::thread::sleep(std::time::Duration::from_millis(5));
fs::write(path, content).expect("Emacs: write target failed");
}
}
}