use broadcast_common::{Parse, Serialize};
macro_rules! declare_segments {
(
$lt:lifetime;
$( $variant:ident = $seg_type: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 AnySegment<$lt> {
$(
#[doc = concat!(stringify!($variant), " segment.")]
$variant($($path)::+ $(<$plt>)?),
)+
Unknown {
segment_type: u8,
page_id: u16,
data: &$lt [u8],
},
Malformed {
segment_type: u8,
page_id: u16,
data: &$lt [u8],
#[cfg_attr(feature = "serde", serde(skip))]
err: crate::error::Error,
},
}
$(
impl<$lt> From<$($path)::+ $(<$plt>)?> for AnySegment<$lt> {
fn from(s: $($path)::+ $(<$plt>)?) -> Self {
Self::$variant(s)
}
}
)+
impl<$lt> AnySegment<$lt> {
pub const DISPATCHED_SEGMENT_TYPES: &'static [u8] = &[$($seg_type),+];
#[must_use]
pub fn name(&self) -> &'static str {
match self {
$(
Self::$variant(_) =>
<$($path)::+ as crate::traits::SegmentDef>::NAME,
)+
Self::Unknown { .. } => "UNKNOWN",
Self::Malformed { .. } => "MALFORMED",
}
}
pub(crate) fn dispatch(segment_type: u8, full: &$lt [u8]) -> Option<crate::Result<Self>> {
match segment_type {
$(
$seg_type => Some(<$($path)::+>::parse(full).map(Self::$variant)),
)+
_ => None,
}
}
pub(crate) fn serialized_len(&self) -> usize {
match self {
$(
Self::$variant(s) => s.serialized_len(),
)+
Self::Unknown { data, .. } => (6 + data.len()),
Self::Malformed { data, .. } => (6 + data.len()),
}
}
pub(crate) fn serialize_into(&self, buf: &mut [u8]) -> crate::Result<usize> {
match self {
$(
Self::$variant(s) => s.serialize_into(buf),
)+
Self::Unknown { segment_type, page_id, data } => {
Self::serialize_raw_segment(*segment_type, *page_id, data, buf)
}
Self::Malformed { segment_type, page_id, data, .. } => {
Self::serialize_raw_segment(*segment_type, *page_id, data, buf)
}
}
}
fn serialize_raw_segment(
segment_type: u8,
page_id: u16,
data: &[u8],
buf: &mut [u8],
) -> crate::Result<usize> {
let len = 6 + data.len();
broadcast_common::len::need_out(buf, len)?;
buf[0] = crate::pes_data_field::SYNC_BYTE;
buf[1] = segment_type;
buf[2..4].copy_from_slice(&page_id.to_be_bytes());
let seg_len = crate::segments::check_segment_length(data.len())?;
buf[4..6].copy_from_slice(&seg_len.to_be_bytes());
buf[6..len].copy_from_slice(data);
Ok(len)
}
}
#[cfg(test)]
mod macro_drift {
#[test]
fn segment_type_literals_match_segment_def() {
use crate::traits::SegmentDef;
$(
assert_eq!(
$seg_type,
<$($path)::+ as SegmentDef>::SEGMENT_TYPE,
concat!("segment_type literal drift for ", stringify!($variant)),
);
assert!(
!<$($path)::+ as SegmentDef>::NAME.is_empty(),
concat!("empty NAME for ", stringify!($variant)),
);
)+
}
}
};
}
declare_segments! {'a;
PageComposition = 0x10 => crate::segments::page_composition::PageCompositionSegment,
RegionComposition = 0x11 => crate::segments::region_composition::RegionCompositionSegment,
ClutDefinition = 0x12 => crate::segments::clut_definition::ClutDefinitionSegment,
ObjectData = 0x13 => crate::segments::object_data::ObjectDataSegment<'a>,
DisplayDefinition = 0x14 => crate::segments::display_definition::DisplayDefinitionSegment,
DisparitySignalling = 0x15 => crate::segments::disparity_signalling::DisparitySignallingSegment,
AlternativeClut = 0x16 => crate::segments::alternative_clut::AlternativeClutSegment,
EndOfDisplaySet = 0x80 => crate::segments::end_of_display_set::EndOfDisplaySetSegment,
Stuffing = 0xFF => crate::segments::stuffing::StuffingSegment<'a>,
}