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