use std::fmt::Display;
use deku::bitvec::{BitVec, Lsb0};
use typed_builder::TypedBuilder;
#[derive(Debug, Clone, PartialEq, Eq, TypedBuilder)]
pub struct Field {
#[builder(setter(transform = |title: impl Display| title.to_string()))]
title: String,
#[builder(setter(transform = |value: impl Display| value.to_string()))]
value: String,
#[builder(default, setter(transform = |byte: u8| Some(byte)))]
byte: Option<u8>,
#[builder(default, setter(transform = |bytes: Vec<u8>| Some(bytes)))]
bytes: Option<Vec<u8>>,
#[builder(default, setter(transform = |bit_range: BitRange| Some(bit_range)))]
bits: Option<BitRange>,
#[builder(default, setter(transform = |units: impl Display| Some(units.to_string())))]
units: Option<String>,
#[builder(default, setter(transform = |subfields: impl IntoIterator<Item = Field>| subfields.into_iter().collect()))]
subfields: Vec<Field>,
}
impl Field {
pub fn new(title: impl Display, value: impl Display) -> Field {
Field::builder()
.title(title.to_string())
.value(value.to_string())
.build()
}
pub fn reserved(bits: BitRange) -> Field {
Field::builder()
.title("Reserved")
.value("---")
.bits(bits)
.build()
}
pub fn title(&self) -> &str {
&self.title
}
pub fn value(&self) -> &str {
&self.value
}
pub fn byte(&self) -> Option<u8> {
self.byte
}
pub fn bytes(&self) -> Option<&[u8]> {
self.bytes.as_deref()
}
pub fn bits(&self) -> Option<String> {
self.bits.as_ref().map(|bits| bits.to_string())
}
pub fn subfields(&self) -> &[Field] {
&self.subfields
}
pub fn units(&self) -> Option<&str> {
self.units.as_deref()
}
}
impl Display for Field {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if !self.subfields.is_empty() {
let mut res = write!(f, "{}: {}", self.title, self.value);
for field in &self.subfields {
res = write!(f, "\n- {}: {}", field.title, field.value);
}
res
} else {
write!(f, "{}: {}", self.title, self.value)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BitRange {
bits: deku::bitvec::BitVec<u8, Lsb0>,
start: usize,
length: usize,
}
impl BitRange {
pub fn new(bytes: &[u8], start: usize, length: usize) -> Self {
let bits = BitVec::<u8, Lsb0>::from_slice(bytes);
Self {
bits,
start,
length,
}
}
pub fn from_byte(byte: u8, start: usize, length: usize) -> Self {
Self::new(&[byte], start, length)
}
}
impl Display for BitRange {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut result = String::new();
let end = self.start + self.length;
let num_bytes = self.bits.len().div_ceil(8);
for byte_idx in (0..num_bytes).rev() {
if !result.is_empty() {
result.push(' ');
}
let byte_start = byte_idx * 8;
let byte_end = ((byte_idx + 1) * 8).min(self.bits.len());
for bit_idx in (byte_start..byte_end).rev() {
if bit_idx >= self.start && bit_idx < end {
result.push(if self.bits[bit_idx] { '1' } else { '0' });
} else {
result.push('.');
}
if (byte_end - bit_idx) % 4 == 0 && bit_idx > byte_start {
result.push(' ');
}
}
}
write!(f, "{}", result)
}
}