pub mod application;
pub mod application_name;
pub mod application_usage;
pub mod dvb_j_application;
pub mod dvb_j_application_location;
pub mod external_application_authorisation;
pub mod simple_application_boundary;
pub mod simple_application_location;
pub mod transport_protocol;
pub use application::ApplicationDescriptor;
pub use application::Visibility;
pub use application_name::ApplicationNameDescriptor;
pub use application_usage::ApplicationUsageDescriptor;
pub use dvb_j_application::DvbJApplicationDescriptor;
pub use dvb_j_application_location::DvbJApplicationLocationDescriptor;
pub use external_application_authorisation::ExternalApplicationAuthorisationDescriptor;
pub use simple_application_boundary::SimpleApplicationBoundaryDescriptor;
pub use simple_application_location::SimpleApplicationLocationDescriptor;
pub use transport_protocol::TransportProtocolDescriptor;
macro_rules! declare_ait_descriptors {
(
$lt:lifetime;
$( $variant:ident = $tag:literal => $($path:ident)::+ $(<$plt:lifetime>)? ),+ $(,)?
) => {
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
#[non_exhaustive]
pub enum AnyAitDescriptor<$lt> {
$(
#[allow(missing_docs)]
$variant($($path)::+ $(<$plt>)?),
)+
Unknown {
tag: u8,
body: &$lt [u8],
},
}
$(
impl<$lt> From<$($path)::+ $(<$plt>)?> for AnyAitDescriptor<$lt> {
fn from(d: $($path)::+ $(<$plt>)?) -> Self {
Self::$variant(d)
}
}
)+
impl<$lt> AnyAitDescriptor<$lt> {
pub const DISPATCHED_TAGS: &'static [u8] = &[$($tag),+];
#[must_use]
pub fn name(&self) -> &'static str {
match self {
$(
Self::$variant(_) =>
<$($path)::+ as crate::traits::DescriptorDef>::NAME,
)+
Self::Unknown { .. } => "UNKNOWN",
}
}
pub(crate) fn dispatch(tag: u8, full: &$lt [u8]) -> Option<crate::error::Result<Self>> {
use dvb_common::Parse;
match tag {
$(
$tag => Some(<$($path)::+>::parse(full).map(Self::$variant)),
)+
_ => None,
}
}
}
#[cfg(test)]
mod macro_drift {
#[test]
fn tag_literals_match_descriptor_def() {
use crate::traits::DescriptorDef;
$(
assert_eq!(
$tag,
<$($path)::+ as DescriptorDef>::TAG,
concat!("tag literal drift for ", stringify!($variant)),
);
assert!(
!<$($path)::+ as DescriptorDef>::NAME.is_empty(),
concat!("empty NAME for ", stringify!($variant)),
);
)+
}
}
};
}
declare_ait_descriptors! {'a;
Application = 0x00 => crate::descriptors::ait::application::ApplicationDescriptor,
ApplicationName = 0x01 => crate::descriptors::ait::application_name::ApplicationNameDescriptor<'a>,
TransportProtocol = 0x02 => crate::descriptors::ait::transport_protocol::TransportProtocolDescriptor<'a>,
DvbJApplication = 0x03 => crate::descriptors::ait::dvb_j_application::DvbJApplicationDescriptor<'a>,
DvbJApplicationLocation = 0x04 => crate::descriptors::ait::dvb_j_application_location::DvbJApplicationLocationDescriptor<'a>,
ExternalAppAuthorisation = 0x05 => crate::descriptors::ait::external_application_authorisation::ExternalApplicationAuthorisationDescriptor,
SimpleAppLocation = 0x15 => crate::descriptors::ait::simple_application_location::SimpleApplicationLocationDescriptor<'a>,
ApplicationUsage = 0x16 => crate::descriptors::ait::application_usage::ApplicationUsageDescriptor,
SimpleAppBoundary = 0x17 => crate::descriptors::ait::simple_application_boundary::SimpleApplicationBoundaryDescriptor<'a>,
}
#[must_use]
pub fn parse_ait_loop(bytes: &[u8]) -> AitDescriptorIter<'_> {
AitDescriptorIter {
bytes,
pos: 0,
fused: false,
}
}
#[derive(Debug, Clone)]
pub struct AitDescriptorIter<'a> {
bytes: &'a [u8],
pos: usize,
fused: bool,
}
impl<'a> Iterator for AitDescriptorIter<'a> {
type Item = crate::error::Result<AnyAitDescriptor<'a>>;
fn next(&mut self) -> Option<Self::Item> {
let (tag, full) = match crate::descriptors::any::next_loop_entry(
self.bytes,
&mut self.pos,
&mut self.fused,
)? {
Ok(v) => v,
Err(e) => return Some(Err(e)),
};
Some(match AnyAitDescriptor::dispatch(tag, full) {
Some(res) => res,
None => Ok(AnyAitDescriptor::Unknown {
tag,
body: &full[2..],
}),
})
}
}
impl core::iter::FusedIterator for AitDescriptorIter<'_> {}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
pub struct AitDescriptorLoop<'a>(&'a [u8]);
impl<'a> AitDescriptorLoop<'a> {
#[must_use]
pub const fn new(raw: &'a [u8]) -> Self {
Self(raw)
}
#[must_use]
pub const fn raw(&self) -> &'a [u8] {
self.0
}
#[must_use]
pub fn iter(&self) -> AitDescriptorIter<'a> {
parse_ait_loop(self.0)
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for AitDescriptorLoop<'_> {
fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
use alloc::vec::Vec;
let items: Vec<crate::error::Result<AnyAitDescriptor<'_>>> = self.iter().collect();
s.collect_seq(items.into_iter().filter_map(|r| r.ok()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn walk_ait_loop_yields_typed_variants() {
let buf = alloc::vec![
0x16, 0x01, 0x01, 0x15, 0x05, b'/', b'a', b'p', b'p', b'/', 0xFE, 0x02, 0xCA, 0xFE,
];
let items: Vec<_> = parse_ait_loop(&buf).collect();
assert_eq!(items.len(), 3);
match items[0].as_ref().unwrap() {
AnyAitDescriptor::ApplicationUsage(au) => {
assert_eq!(au.usage_type, 0x01);
}
other => panic!("expected ApplicationUsage, got {other:?}"),
}
match items[1].as_ref().unwrap() {
AnyAitDescriptor::SimpleAppLocation(loc) => {
assert_eq!(loc.initial_path_bytes.raw(), b"/app/");
}
other => panic!("expected SimpleAppLocation, got {other:?}"),
}
match items[2].as_ref().unwrap() {
AnyAitDescriptor::Unknown { tag, body } => {
assert_eq!(*tag, 0xFE);
assert_eq!(*body, &[0xCA, 0xFE]);
}
other => panic!("expected Unknown, got {other:?}"),
}
}
#[test]
fn ait_descriptor_loop_iter() {
let buf = [0x16, 0x01, 0x01];
let loop_ = AitDescriptorLoop::new(&buf);
let items: Vec<_> = loop_.iter().collect();
assert_eq!(items.len(), 1);
assert!(matches!(
items[0].as_ref().unwrap(),
AnyAitDescriptor::ApplicationUsage(_)
));
assert_eq!(loop_.raw(), &buf[..]);
}
#[test]
fn dispatched_tags_covers_all_known() {
assert_eq!(
AnyAitDescriptor::DISPATCHED_TAGS,
&[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x15, 0x16, 0x17]
);
}
}