use crate::error::{CrousError, Result};
pub const MAGIC: &[u8; 7] = b"CROUSv1";
pub const HEADER_SIZE: usize = 8;
pub const FLAGS_NONE: u8 = 0x00;
pub const FLAGS_HAS_INDEX: u8 = 0x02;
pub const FLAGS_HAS_SCHEMA: u8 = 0x04;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FileHeader {
pub flags: u8,
}
impl FileHeader {
pub fn new(flags: u8) -> Self {
Self { flags }
}
pub fn encode(&self) -> [u8; HEADER_SIZE] {
let mut buf = [0u8; HEADER_SIZE];
buf[..7].copy_from_slice(MAGIC);
buf[7] = self.flags;
buf
}
pub fn decode(data: &[u8]) -> Result<Self> {
if data.len() < HEADER_SIZE {
return Err(CrousError::UnexpectedEof(data.len()));
}
if &data[..7] != MAGIC {
return Err(CrousError::InvalidMagic);
}
Ok(Self { flags: data[7] })
}
pub fn has_index(&self) -> bool {
self.flags & FLAGS_HAS_INDEX != 0
}
pub fn has_schema(&self) -> bool {
self.flags & FLAGS_HAS_SCHEMA != 0
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn header_roundtrip() {
let hdr = FileHeader::new(FLAGS_HAS_INDEX | FLAGS_HAS_SCHEMA);
let bytes = hdr.encode();
assert_eq!(&bytes[..7], b"CROUSv1");
assert_eq!(bytes[7], 0x06);
let decoded = FileHeader::decode(&bytes).unwrap();
assert_eq!(decoded, hdr);
assert!(decoded.has_index());
assert!(decoded.has_schema());
}
#[test]
fn header_invalid_magic() {
let bad = b"CROUSv2\x00";
assert!(matches!(
FileHeader::decode(bad),
Err(CrousError::InvalidMagic)
));
}
#[test]
fn header_too_short() {
let short = b"CROUS";
assert!(FileHeader::decode(short).is_err());
}
}