use std::{fmt::Write, path::PathBuf};
#[derive(Debug)]
pub struct RecoverySnapshot {
pub unsaved_buffers: Vec<UnsavedBuffer>,
pub timestamp: std::time::SystemTime,
}
#[derive(Debug)]
pub struct UnsavedBuffer {
pub id: u32,
pub path: Option<PathBuf>,
pub content_hash: u64,
pub line_count: usize,
}
#[must_use]
pub fn recovery_dir() -> PathBuf {
reovim_arch::dirs::data_local_dir()
.unwrap_or_else(|| PathBuf::from("."))
.join("reovim")
.join("recovery")
}
pub fn save_buffer_for_recovery(
buffer_id: u32,
original_path: Option<&std::path::Path>,
content: &str,
) -> std::io::Result<PathBuf> {
let dir = recovery_dir();
std::fs::create_dir_all(&dir)?;
let filename = format!(
"buffer-{}-{}.txt",
buffer_id,
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.map_or(0, |d| d.as_secs())
);
let path = dir.join(&filename);
let mut output = String::new();
writeln!(output, "# Recovery file for buffer {buffer_id}").ok();
if let Some(orig) = original_path {
writeln!(output, "# Original path: {}", orig.display()).ok();
}
writeln!(output, "# Lines: {}", content.lines().count()).ok();
output.push_str("# ---\n");
output.push_str(content);
std::fs::write(&path, output)?;
Ok(path)
}
#[cfg_attr(coverage_nightly, coverage(off))]
pub fn list_recovery_files() -> std::io::Result<Vec<PathBuf>> {
let dir = recovery_dir();
if !dir.exists() {
return Ok(Vec::new());
}
let mut files: Vec<(PathBuf, std::time::SystemTime)> = Vec::new();
for entry in std::fs::read_dir(dir)? {
let entry = entry?;
let path = entry.path();
if path.extension().is_some_and(|e| e == "txt")
&& let Ok(meta) = std::fs::metadata(&path)
&& let Ok(modified) = meta.modified()
{
files.push((path, modified));
}
}
files.sort_by_key(|b| std::cmp::Reverse(b.1));
Ok(files.into_iter().map(|(p, _)| p).collect())
}
#[cfg_attr(coverage_nightly, coverage(off))]
pub fn cleanup_old_recovery_files(max_age_secs: u64) -> std::io::Result<usize> {
let now = std::time::SystemTime::now();
let mut removed = 0;
for path in list_recovery_files()? {
if let Ok(meta) = std::fs::metadata(&path)
&& let Ok(modified) = meta.modified()
&& let Ok(age) = now.duration_since(modified)
&& age.as_secs() > max_age_secs
{
std::fs::remove_file(&path)?;
removed += 1;
}
}
Ok(removed)
}