use std::{fs::OpenOptions, path::Path};
use fwob_core::KeyType;
use crate::{file_header::update_counts, Reader, Result, FILE_HEADER_LEN};
pub fn repair_committed_tail(path: impl AsRef<Path>) -> Result<()> {
let path = path.as_ref();
let metadata_len = std::fs::metadata(path)?.len();
let mut reader = Reader::open(path)?;
let header = reader.header().clone();
if header.page_size == 0 || metadata_len < FILE_HEADER_LEN {
return Err(crate::V2Error::InvalidFileHeader);
}
let physical_pages = (metadata_len - FILE_HEADER_LEN) / u64::from(header.page_size);
let candidate_pages = header.page_count.min(physical_pages);
let key_type = KeyType::from_field(header.schema.key_field())?;
let mut valid_pages = 0u64;
let mut frame_count = 0u64;
let mut last_key = None;
for page_index in 0..candidate_pages {
let page = match reader.read_page_header(page_index) {
Ok(page) if page.first_frame_index == frame_count => page,
_ => break,
};
let frames = match reader.read_page_frames(page_index) {
Ok(frames) => frames,
Err(_) => break,
};
if frames.len() != page.frame_count as usize {
break;
}
let mut page_valid = true;
for frame in &frames {
let key = frame.as_ref().key(&header.schema, key_type)?;
if last_key.is_some_and(|last| key < last) {
page_valid = false;
break;
}
last_key = Some(key);
}
if !page_valid {
break;
}
frame_count += frames.len() as u64;
valid_pages += 1;
}
drop(reader);
let repaired_len = FILE_HEADER_LEN + valid_pages * u64::from(header.page_size);
let mut file = OpenOptions::new().read(true).write(true).open(path)?;
file.set_len(repaired_len)?;
update_counts(&mut file, valid_pages, frame_count)?;
drop(file);
Reader::open(path)?.verify()
}