use crate::error::{Error, Result};
pub const LINE_HEADER_LEN: usize = 1;
pub const RESERVED_PREFIX: u8 = 0b11;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct LineHeader {
pub field_parity: bool,
pub line_offset: u8,
}
impl LineHeader {
pub fn new(field_parity: bool, line_offset: u8) -> Self {
LineHeader {
field_parity,
line_offset,
}
}
pub fn from_byte(byte: u8) -> Self {
LineHeader {
field_parity: (byte & 0b0010_0000) != 0,
line_offset: byte & 0b0001_1111,
}
}
pub fn to_byte(self) -> Result<u8> {
if self.line_offset > 0b0001_1111 {
return Err(Error::FieldTooWide {
what: "line_offset",
value: self.line_offset as u32,
bits: 5,
});
}
let mut b = RESERVED_PREFIX << 6;
if self.field_parity {
b |= 0b0010_0000;
}
b |= self.line_offset;
Ok(b)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_all_bytes() {
for raw in 0u16..=0xFF {
let raw = raw as u8;
let h = LineHeader::from_byte(raw);
let re = h.to_byte().unwrap();
assert_eq!(re & 0b0011_1111, raw & 0b0011_1111, "raw={raw:#04X}");
assert_eq!(re >> 6, RESERVED_PREFIX);
}
}
#[test]
fn field_split() {
let h = LineHeader::from_byte(0xD0);
assert!(!h.field_parity);
assert_eq!(h.line_offset, 16);
assert_eq!(h.to_byte().unwrap(), 0xD0);
let h = LineHeader::new(true, 21);
assert_eq!(h.to_byte().unwrap(), 0xF5);
}
#[test]
fn rejects_overwide_line_offset() {
assert!(matches!(
LineHeader::new(true, 32).to_byte(),
Err(Error::FieldTooWide { .. })
));
}
}