use std::fs::OpenOptions;
use std::io;
pub fn truncate_to_last_valid(path: &std::path::Path, valid_end_offset: u64) -> io::Result<()> {
let file = OpenOptions::new().write(true).open(path)?;
file.set_len(valid_end_offset)?;
file.sync_all()?;
Ok(())
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum RepairPolicy {
#[default]
Strict,
TruncatePartial,
}
#[cfg(test)]
mod tests {
use std::fs;
use std::io::Write;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::*;
static TEST_COUNTER: AtomicUsize = AtomicUsize::new(0);
fn temp_path() -> std::path::PathBuf {
let dir = std::env::temp_dir();
let count = TEST_COUNTER.fetch_add(1, Ordering::SeqCst);
let path =
dir.join(format!("actionqueue_wal_repair_test_{}_{}.tmp", std::process::id(), count));
let _ = fs::remove_file(&path);
path
}
#[test]
fn truncate_removes_trailing_bytes() {
let path = temp_path();
{
let mut file = fs::File::create(&path).unwrap();
file.write_all(b"valid_record_data_here_extra_junk").unwrap();
}
truncate_to_last_valid(&path, 22).unwrap();
let contents = fs::read(&path).unwrap();
assert_eq!(&contents, b"valid_record_data_here");
let _ = fs::remove_file(&path);
}
#[test]
fn truncate_to_zero_empties_file() {
let path = temp_path();
{
let mut file = fs::File::create(&path).unwrap();
file.write_all(b"some data").unwrap();
}
truncate_to_last_valid(&path, 0).unwrap();
let contents = fs::read(&path).unwrap();
assert!(contents.is_empty());
let _ = fs::remove_file(&path);
}
}