use crate::psi::{
DescriptorRef,
PsiSectionError,
};
#[derive(Debug, Clone, Copy)]
pub struct ContentItemRef {
nibble: u8,
user: u8,
}
impl ContentItemRef {
pub fn content_nibble_level_1(&self) -> u8 {
(self.nibble & 0xf0) >> 4
}
pub fn content_nibble_level_2(&self) -> u8 {
self.nibble & 0x0f
}
pub fn user_nibble_1(&self) -> u8 {
(self.user & 0xf0) >> 4
}
pub fn user_nibble_2(&self) -> u8 {
self.user & 0x0f
}
}
pub struct ContentItemIter<'a> {
data: &'a [u8],
offset: usize,
}
impl<'a> Iterator for ContentItemIter<'a> {
type Item = ContentItemRef;
fn next(&mut self) -> Option<Self::Item> {
if self.offset + 2 > self.data.len() {
return None;
}
let out = ContentItemRef {
nibble: self.data[self.offset],
user: self.data[self.offset + 1],
};
self.offset += 2;
Some(out)
}
}
#[derive(Debug, Clone, Copy)]
pub struct ContentDescriptorRef<'a>(&'a [u8]);
impl<'a> ContentDescriptorRef<'a> {
pub const TAG: u8 = 0x54;
pub fn items(&self) -> ContentItemIter<'a> {
ContentItemIter {
data: self.0,
offset: 0,
}
}
}
impl<'a> TryFrom<DescriptorRef<'a>> for ContentDescriptorRef<'a> {
type Error = PsiSectionError;
fn try_from(descriptor: DescriptorRef<'a>) -> Result<Self, Self::Error> {
if descriptor.tag() != Self::TAG {
return Err(PsiSectionError::InvalidDescriptorTag);
}
Ok(ContentDescriptorRef(descriptor.data()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::psi::DescriptorsRef;
fn descriptor(tag: u8, payload: &[u8]) -> Vec<u8> {
let mut v = vec![tag, payload.len() as u8];
v.extend_from_slice(payload);
v
}
fn first(bytes: &[u8]) -> DescriptorRef<'_> {
DescriptorsRef::from(bytes)
.into_iter()
.next()
.unwrap()
.unwrap()
}
#[test]
fn parses_entries() {
let bytes = descriptor(0x54, &[0x10, 0x00, 0x25, 0xff]);
let cd = ContentDescriptorRef::try_from(first(&bytes)).unwrap();
let entries: Vec<_> = cd.items().collect();
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].content_nibble_level_1(), 1);
assert_eq!(entries[1].content_nibble_level_1(), 2);
assert_eq!(entries[1].content_nibble_level_2(), 5);
assert_eq!(entries[1].user_nibble_1(), 0x0f);
}
#[test]
fn ignores_trailing_odd_byte() {
let bytes = descriptor(0x54, &[0x10, 0x00, 0x20]);
let cd = ContentDescriptorRef::try_from(first(&bytes)).unwrap();
assert_eq!(cd.items().count(), 1);
}
}