1pub mod application;
43pub mod application_name;
44pub mod application_usage;
45pub mod dvb_j_application;
46pub mod dvb_j_application_location;
47pub mod external_application_authorisation;
48pub mod simple_application_boundary;
49pub mod simple_application_location;
50pub mod transport_protocol;
51
52pub use application::ApplicationDescriptor;
53pub use application::Visibility;
54pub use application_name::ApplicationNameDescriptor;
55pub use application_usage::ApplicationUsageDescriptor;
56pub use dvb_j_application::DvbJApplicationDescriptor;
57pub use dvb_j_application_location::DvbJApplicationLocationDescriptor;
58pub use external_application_authorisation::ExternalApplicationAuthorisationDescriptor;
59pub use simple_application_boundary::SimpleApplicationBoundaryDescriptor;
60pub use simple_application_location::SimpleApplicationLocationDescriptor;
61pub use transport_protocol::TransportProtocolDescriptor;
62
63macro_rules! declare_ait_descriptors {
68 (
69 $lt:lifetime;
70 $( $variant:ident = $tag:literal => $($path:ident)::+ $(<$plt:lifetime>)? ),+ $(,)?
71 ) => {
72 #[derive(Debug)]
78 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
79 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
80 #[non_exhaustive]
81 pub enum AnyAitDescriptor<$lt> {
82 $(
83 #[allow(missing_docs)]
84 $variant($($path)::+ $(<$plt>)?),
85 )+
86 Unknown {
89 tag: u8,
91 body: &$lt [u8],
93 },
94 }
95
96 $(
97 impl<$lt> From<$($path)::+ $(<$plt>)?> for AnyAitDescriptor<$lt> {
98 fn from(d: $($path)::+ $(<$plt>)?) -> Self {
99 Self::$variant(d)
100 }
101 }
102 )+
103
104 impl<$lt> AnyAitDescriptor<$lt> {
105 pub const DISPATCHED_TAGS: &'static [u8] = &[$($tag),+];
108
109 #[must_use]
114 pub fn name(&self) -> &'static str {
115 match self {
116 $(
117 Self::$variant(_) =>
118 <$($path)::+ as crate::traits::DescriptorDef>::NAME,
119 )+
120 Self::Unknown { .. } => "UNKNOWN",
121 }
122 }
123
124 pub(crate) fn dispatch(tag: u8, full: &$lt [u8]) -> Option<crate::error::Result<Self>> {
130 use dvb_common::Parse;
131 match tag {
132 $(
133 $tag => Some(<$($path)::+>::parse(full).map(Self::$variant)),
134 )+
135 _ => None,
136 }
137 }
138 }
139
140 #[cfg(test)]
141 mod macro_drift {
142 #[test]
143 fn tag_literals_match_descriptor_def() {
144 use crate::traits::DescriptorDef;
145 $(
146 assert_eq!(
147 $tag,
148 <$($path)::+ as DescriptorDef>::TAG,
149 concat!("tag literal drift for ", stringify!($variant)),
150 );
151 assert!(
152 !<$($path)::+ as DescriptorDef>::NAME.is_empty(),
153 concat!("empty NAME for ", stringify!($variant)),
154 );
155 )+
156 }
157 }
158 };
159}
160
161declare_ait_descriptors! {'a;
162 Application = 0x00 => crate::descriptors::ait::application::ApplicationDescriptor,
163 ApplicationName = 0x01 => crate::descriptors::ait::application_name::ApplicationNameDescriptor<'a>,
164 TransportProtocol = 0x02 => crate::descriptors::ait::transport_protocol::TransportProtocolDescriptor<'a>,
165 DvbJApplication = 0x03 => crate::descriptors::ait::dvb_j_application::DvbJApplicationDescriptor<'a>,
166 DvbJApplicationLocation = 0x04 => crate::descriptors::ait::dvb_j_application_location::DvbJApplicationLocationDescriptor<'a>,
167 ExternalAppAuthorisation = 0x05 => crate::descriptors::ait::external_application_authorisation::ExternalApplicationAuthorisationDescriptor,
168 SimpleAppLocation = 0x15 => crate::descriptors::ait::simple_application_location::SimpleApplicationLocationDescriptor<'a>,
169 ApplicationUsage = 0x16 => crate::descriptors::ait::application_usage::ApplicationUsageDescriptor,
170 SimpleAppBoundary = 0x17 => crate::descriptors::ait::simple_application_boundary::SimpleApplicationBoundaryDescriptor<'a>,
171}
172
173#[must_use]
180pub fn parse_ait_loop(bytes: &[u8]) -> AitDescriptorIter<'_> {
181 AitDescriptorIter {
182 bytes,
183 pos: 0,
184 fused: false,
185 }
186}
187
188#[derive(Debug, Clone)]
190pub struct AitDescriptorIter<'a> {
191 bytes: &'a [u8],
192 pos: usize,
193 fused: bool,
194}
195
196impl<'a> Iterator for AitDescriptorIter<'a> {
197 type Item = crate::error::Result<AnyAitDescriptor<'a>>;
198
199 fn next(&mut self) -> Option<Self::Item> {
200 let (tag, full) = match crate::descriptors::any::next_loop_entry(
201 self.bytes,
202 &mut self.pos,
203 &mut self.fused,
204 )? {
205 Ok(v) => v,
206 Err(e) => return Some(Err(e)),
207 };
208 Some(match AnyAitDescriptor::dispatch(tag, full) {
209 Some(res) => res,
210 None => Ok(AnyAitDescriptor::Unknown {
211 tag,
212 body: &full[2..],
213 }),
214 })
215 }
216}
217
218impl core::iter::FusedIterator for AitDescriptorIter<'_> {}
219
220#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
225#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
226pub struct AitDescriptorLoop<'a>(&'a [u8]);
227
228impl<'a> AitDescriptorLoop<'a> {
229 #[must_use]
232 pub const fn new(raw: &'a [u8]) -> Self {
233 Self(raw)
234 }
235
236 #[must_use]
238 pub const fn raw(&self) -> &'a [u8] {
239 self.0
240 }
241
242 #[must_use]
246 pub fn iter(&self) -> AitDescriptorIter<'a> {
247 parse_ait_loop(self.0)
248 }
249}
250
251#[cfg(feature = "serde")]
252impl serde::Serialize for AitDescriptorLoop<'_> {
253 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
254 use alloc::vec::Vec;
255 let items: Vec<crate::error::Result<AnyAitDescriptor<'_>>> = self.iter().collect();
256 s.collect_seq(items.into_iter().filter_map(|r| r.ok()))
257 }
258}
259
260#[cfg(test)]
261mod tests {
262 use super::*;
263
264 #[test]
265 fn walk_ait_loop_yields_typed_variants() {
266 let buf = alloc::vec![
268 0x16, 0x01, 0x01, 0x15, 0x05, b'/', b'a', b'p', b'p', b'/', 0xFE, 0x02, 0xCA, 0xFE,
269 ];
270 let items: Vec<_> = parse_ait_loop(&buf).collect();
271 assert_eq!(items.len(), 3);
272
273 match items[0].as_ref().unwrap() {
274 AnyAitDescriptor::ApplicationUsage(au) => {
275 assert_eq!(au.usage_type, 0x01);
276 }
277 other => panic!("expected ApplicationUsage, got {other:?}"),
278 }
279
280 match items[1].as_ref().unwrap() {
281 AnyAitDescriptor::SimpleAppLocation(loc) => {
282 assert_eq!(loc.initial_path_bytes.raw(), b"/app/");
283 }
284 other => panic!("expected SimpleAppLocation, got {other:?}"),
285 }
286
287 match items[2].as_ref().unwrap() {
288 AnyAitDescriptor::Unknown { tag, body } => {
289 assert_eq!(*tag, 0xFE);
290 assert_eq!(*body, &[0xCA, 0xFE]);
291 }
292 other => panic!("expected Unknown, got {other:?}"),
293 }
294 }
295
296 #[test]
297 fn ait_descriptor_loop_iter() {
298 let buf = [0x16, 0x01, 0x01];
299 let loop_ = AitDescriptorLoop::new(&buf);
300 let items: Vec<_> = loop_.iter().collect();
301 assert_eq!(items.len(), 1);
302 assert!(matches!(
303 items[0].as_ref().unwrap(),
304 AnyAitDescriptor::ApplicationUsage(_)
305 ));
306 assert_eq!(loop_.raw(), &buf[..]);
307 }
308
309 #[test]
310 fn dispatched_tags_covers_all_known() {
311 assert_eq!(
312 AnyAitDescriptor::DISPATCHED_TAGS,
313 &[0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x15, 0x16, 0x17]
314 );
315 }
316}