use crate::database::manifest::ManifestRec;
use std::{
fs::File,
io::{self, Read},
path::Path,
};
pub struct ManifestReader {
file: File,
}
impl ManifestReader {
pub fn open(path: &Path) -> io::Result<Self> {
Ok(Self {
file: File::open(path)?,
})
}
pub fn read_next(&mut self) -> io::Result<Option<ManifestRec>> {
let mut tag = [0u8; 1];
match self.file.read_exact(&mut tag) {
Ok(()) => {}
Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => return Ok(None),
Err(e) => return Err(e),
}
let mut len_buf = [0u8; 4];
if let Err(e) = self.file.read_exact(&mut len_buf) {
if e.kind() == io::ErrorKind::UnexpectedEof {
return Ok(None);
}
return Err(e);
}
let payload_len = u32::from_le_bytes(len_buf) as usize;
let mut payload = vec![0u8; payload_len];
if let Err(e) = self.file.read_exact(&mut payload) {
if e.kind() == io::ErrorKind::UnexpectedEof {
return Ok(None);
}
return Err(e);
}
let mut crc_buf = [0u8; 4];
if let Err(e) = self.file.read_exact(&mut crc_buf) {
if e.kind() == io::ErrorKind::UnexpectedEof {
return Ok(None);
}
return Err(e);
}
let stored_crc = u32::from_le_bytes(crc_buf);
let mut record_bytes = Vec::with_capacity(1 + 4 + payload_len);
record_bytes.extend_from_slice(&tag);
record_bytes.extend_from_slice(&len_buf);
record_bytes.extend_from_slice(&payload);
let computed_crc = crc32fast::hash(&record_bytes);
if stored_crc != computed_crc {
return Err(io::Error::new(
io::ErrorKind::InvalidData,
format!(
"manifest record CRC mismatch: stored {stored_crc:#010x}, \
computed {computed_crc:#010x}"
),
));
}
ManifestRec::decode(&mut &record_bytes[..]).map(Some)
}
}