use crate::error::Result;
macro_rules! declare_splice_descriptors {
(
$lt:lifetime;
$( $variant:ident = $tag:literal => $($path:ident)::+ $(<$plt:lifetime>)? ),+ $(,)?
) => {
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[non_exhaustive]
pub enum AnySpliceDescriptor<$lt> {
$(
#[allow(missing_docs)]
$variant($($path)::+ $(<$plt>)?),
)+
Unknown {
tag: u8,
body: &$lt [u8],
},
}
$(
impl<$lt> From<$($path)::+ $(<$plt>)?> for AnySpliceDescriptor<$lt> {
fn from(d: $($path)::+ $(<$plt>)?) -> Self {
Self::$variant(d)
}
}
)+
impl<$lt> AnySpliceDescriptor<$lt> {
pub const DISPATCHED_TAGS: &'static [u8] = &[$($tag),+];
#[must_use]
pub fn name(&self) -> &'static str {
match self {
$(
Self::$variant(_) =>
<$($path)::+ as crate::traits::SpliceDescriptorDef>::NAME,
)+
Self::Unknown { .. } => "UNKNOWN",
}
}
pub(crate) fn dispatch(tag: u8, full: &$lt [u8]) -> Result<Self> {
use dvb_common::Parse;
match tag {
$(
$tag => <$($path)::+>::parse(full).map(Self::$variant),
)+
_ => Ok(Self::Unknown { tag, body: &full[2..] }),
}
}
#[must_use]
pub fn serialized_len(&self) -> usize {
use dvb_common::Serialize;
match self {
$(
Self::$variant(d) => d.serialized_len(),
)+
Self::Unknown { body, .. } => 2 + body.len(),
}
}
pub fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
use dvb_common::Serialize;
match self {
$(
Self::$variant(d) => d.serialize_into(buf),
)+
Self::Unknown { tag, body } => {
let need = 2 + body.len();
if buf.len() < need {
return Err(crate::error::Error::OutputBufferTooSmall {
need,
have: buf.len(),
});
}
buf[0] = *tag;
buf[1] = body.len() as u8;
buf[2..need].copy_from_slice(body);
Ok(need)
}
}
}
}
#[cfg(test)]
mod macro_drift {
#[test]
fn tag_literals_match_descriptor_def() {
use crate::traits::SpliceDescriptorDef;
$(
assert_eq!(
$tag,
<$($path)::+ as SpliceDescriptorDef>::TAG,
concat!("tag literal drift for ", stringify!($variant)),
);
assert!(
!<$($path)::+ as SpliceDescriptorDef>::NAME.is_empty(),
concat!("empty NAME for ", stringify!($variant)),
);
)+
}
}
};
}
declare_splice_descriptors! {'a;
Avail = 0x00 => crate::descriptors::avail::AvailDescriptor,
Dtmf = 0x01 => crate::descriptors::dtmf::DtmfDescriptor,
Segmentation = 0x02 => crate::descriptors::segmentation::SegmentationDescriptor<'a>,
Time = 0x03 => crate::descriptors::time::TimeDescriptor,
Audio = 0x04 => crate::descriptors::audio::AudioDescriptor,
DvbDas = 0xF0 => crate::dvb_ta::das_descriptor::DvbDasDescriptor<'a>,
}
#[must_use]
pub fn parse_loop(bytes: &[u8]) -> SpliceDescriptorIter<'_> {
SpliceDescriptorIter {
bytes,
pos: 0,
fused: false,
}
}
#[derive(Debug, Clone)]
pub struct SpliceDescriptorIter<'a> {
bytes: &'a [u8],
pos: usize,
fused: bool,
}
impl<'a> Iterator for SpliceDescriptorIter<'a> {
type Item = Result<AnySpliceDescriptor<'a>>;
fn next(&mut self) -> Option<Self::Item> {
if self.fused || self.pos >= self.bytes.len() {
return None;
}
let rem = &self.bytes[self.pos..];
if rem.len() < 2 {
self.fused = true;
return Some(Err(crate::error::Error::BufferTooShort {
need: 2,
have: rem.len(),
what: "splice_descriptor header in loop",
}));
}
let tag = rem[0];
let len = rem[1] as usize;
let total = 2 + len;
if rem.len() < total {
self.fused = true;
return Some(Err(crate::error::Error::BufferTooShort {
need: total,
have: rem.len(),
what: "splice_descriptor body in loop",
}));
}
let full = &rem[..total];
self.pos += total;
Some(AnySpliceDescriptor::dispatch(tag, full))
}
}
impl core::iter::FusedIterator for SpliceDescriptorIter<'_> {}
#[cfg(test)]
mod tests {
use super::*;
use crate::descriptors::avail::AvailDescriptor;
use crate::descriptors::header::CUEI;
use dvb_common::Serialize;
#[test]
fn loop_dispatches_typed_and_unknown() {
let avail = AvailDescriptor {
identifier: CUEI,
provider_avail_id: 0x1111_2222,
}
.to_bytes();
let unknown = [0xEE, 0x02, 0xAB, 0xCD];
let mut loop_bytes = avail.clone();
loop_bytes.extend_from_slice(&unknown);
let items: Vec<_> = parse_loop(&loop_bytes).collect::<Result<_>>().unwrap();
assert_eq!(items.len(), 2);
assert!(matches!(items[0], AnySpliceDescriptor::Avail(_)));
assert_eq!(items[0].name(), "AVAIL");
match &items[1] {
AnySpliceDescriptor::Unknown { tag: 0xEE, body } => assert_eq!(*body, &[0xAB, 0xCD]),
other => panic!("expected Unknown, got {other:?}"),
}
let mut out = Vec::new();
for d in &items {
let mut b = vec![0u8; d.serialized_len()];
d.serialize_into(&mut b).unwrap();
out.extend_from_slice(&b);
}
assert_eq!(out, loop_bytes);
}
#[test]
fn truncated_final_entry_yields_err_then_fuses() {
let bytes = [0x00, 0x08, 0x43, 0x55]; let mut it = parse_loop(&bytes);
assert!(it.next().unwrap().is_err());
assert!(it.next().is_none());
}
}