1use alloc::boxed::Box;
50#[cfg(feature = "serde")]
51use alloc::string::ToString;
52
53macro_rules! declare_descriptors {
61 (
62 $lt:lifetime;
63 $( $variant:ident = $tag:literal => $($path:ident)::+ $(<$plt:lifetime>)? ),+ $(,)?
64 $( ; @no_dispatch $( $nd_variant:ident => $($nd_path:ident)::+ $(<$nd_plt:lifetime>)? ),+ $(,)? )?
65 ) => {
66 #[derive(Debug)]
73 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
74 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
75 #[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
80 #[non_exhaustive]
81 pub enum AnyDescriptor<$lt> {
82 $(
83 #[allow(missing_docs)]
84 $variant($($path)::+ $(<$plt>)?),
85 )+
86 $($(
87 #[allow(missing_docs)]
88 $nd_variant($($nd_path)::+ $(<$nd_plt>)?),
89 )+)?
90 Other {
94 tag: u8,
96 #[cfg_attr(
100 feature = "serde",
101 serde(serialize_with = "crate::descriptors::registry::serialize_erased")
102 )]
103 value: Box<dyn crate::descriptors::registry::DescriptorObject>,
104 },
105 Unknown {
108 tag: u8,
110 body: &$lt [u8],
112 },
113 }
114
115 $(
116 impl<$lt> From<$($path)::+ $(<$plt>)?> for AnyDescriptor<$lt> {
117 fn from(d: $($path)::+ $(<$plt>)?) -> Self {
118 Self::$variant(d)
119 }
120 }
121 )+
122 $($(
123 impl<$lt> From<$($nd_path)::+ $(<$nd_plt>)?> for AnyDescriptor<$lt> {
124 fn from(d: $($nd_path)::+ $(<$nd_plt>)?) -> Self {
125 Self::$nd_variant(d)
126 }
127 }
128 )+)?
129
130 impl<$lt> AnyDescriptor<$lt> {
131 pub const DISPATCHED_TAGS: &'static [u8] = &[$($tag),+];
134
135 #[must_use]
141 pub fn name(&self) -> &'static str {
142 match self {
143 $(
144 Self::$variant(_) =>
145 <$($path)::+ as crate::traits::DescriptorDef>::NAME,
146 )+
147 $($(
148 Self::$nd_variant(_) =>
149 <$($nd_path)::+ as crate::traits::DescriptorDef>::NAME,
150 )+)?
151 Self::Other { .. } => "CUSTOM",
152 Self::Unknown { .. } => "UNKNOWN",
153 }
154 }
155
156 #[doc(hidden)]
161 pub fn reserialize(&self) -> Option<core::result::Result<alloc::vec::Vec<u8>, alloc::string::String>> {
162 use broadcast_common::Serialize;
163 fn go<S: Serialize>(d: &S) -> core::result::Result<alloc::vec::Vec<u8>, alloc::string::String>
164 where
165 S::Error: core::fmt::Debug,
166 {
167 let mut out = alloc::vec![0u8; d.serialized_len()];
168 d.serialize_into(&mut out).map_err(|e| alloc::format!("{e:?}"))?;
169 Ok(out)
170 }
171 match self {
172 $( Self::$variant(d) => Some(go(d)), )+
173 $($( Self::$nd_variant(d) => Some(go(d)), )+)?
174 Self::Other { .. } => None,
175 Self::Unknown { tag, body } => {
176 Some(
177 broadcast_common::len::fit_u8(body.len(), "descriptor_length")
178 .map(|n| {
179 let mut v = alloc::vec![*tag, n];
180 v.extend_from_slice(body);
181 v
182 })
183 .map_err(|e| alloc::format!("{e:?}")),
184 )
185 }
186 }
187 }
188
189 pub(crate) fn dispatch(tag: u8, full: &$lt [u8]) -> Option<crate::Result<Self>> {
195 use broadcast_common::Parse;
196 match tag {
197 $(
198 $tag => Some(<$($path)::+>::parse(full).map(Self::$variant)),
199 )+
200 _ => None,
201 }
202 }
203 }
204
205 #[cfg(test)]
206 mod macro_drift {
207 #[test]
208 fn tag_literals_match_descriptor_def() {
209 use crate::traits::DescriptorDef;
210 $(
211 assert_eq!(
212 $tag,
213 <$($path)::+ as DescriptorDef>::TAG,
214 concat!("tag literal drift for ", stringify!($variant)),
215 );
216 assert!(
217 !<$($path)::+ as DescriptorDef>::NAME.is_empty(),
218 concat!("empty NAME for ", stringify!($variant)),
219 );
220 )+
221 $($(
222 assert!(
223 !<$($nd_path)::+ as DescriptorDef>::NAME.is_empty(),
224 concat!("empty NAME for ", stringify!($nd_variant)),
225 );
226 )+)?
227 }
228 }
229 };
230}
231
232declare_descriptors! {'a;
233 VideoStream = 0x02 => crate::descriptors::video_stream::VideoStreamDescriptor,
235 AudioStream = 0x03 => crate::descriptors::audio_stream::AudioStreamDescriptor,
236 Hierarchy = 0x04 => crate::descriptors::hierarchy::HierarchyDescriptor,
237 Registration = 0x05 => crate::descriptors::registration::RegistrationDescriptor<'a>,
238 DataStreamAlignment = 0x06 => crate::descriptors::data_stream_alignment::DataStreamAlignmentDescriptor,
239 TargetBackgroundGrid = 0x07 => crate::descriptors::target_background_grid::TargetBackgroundGridDescriptor,
240 VideoWindow = 0x08 => crate::descriptors::video_window::VideoWindowDescriptor,
241 Ca = 0x09 => crate::descriptors::ca::CaDescriptor<'a>,
242 Iso639Language = 0x0A => crate::descriptors::iso_639_language::Iso639LanguageDescriptor,
243 SystemClock = 0x0B => crate::descriptors::system_clock::SystemClockDescriptor,
244 MultiplexBufferUtilization = 0x0C => crate::descriptors::multiplex_buffer_utilization::MultiplexBufferUtilizationDescriptor,
245 Copyright = 0x0D => crate::descriptors::copyright::CopyrightDescriptor<'a>,
246 MaximumBitrate = 0x0E => crate::descriptors::maximum_bitrate::MaximumBitrateDescriptor,
247 PrivateDataIndicator = 0x0F => crate::descriptors::private_data_indicator::PrivateDataIndicatorDescriptor,
248 SmoothingBuffer = 0x10 => crate::descriptors::smoothing_buffer::SmoothingBufferDescriptor,
249 Std = 0x11 => crate::descriptors::std::StdDescriptor,
250 Ibp = 0x12 => crate::descriptors::ibp::IbpDescriptor,
251 CarouselIdentifier = 0x13 => crate::descriptors::carousel_identifier::CarouselIdentifierDescriptor<'a>,
252 AssociationTag = 0x14 => crate::descriptors::association_tag::AssociationTagDescriptor<'a>,
253 DeferredAssociationTags = 0x15 => crate::descriptors::deferred_association_tags::DeferredAssociationTagsDescriptor<'a>,
254 Mpeg4Video = 0x1B => crate::descriptors::mpeg4_video::Mpeg4VideoDescriptor,
255 Mpeg4Audio = 0x1C => crate::descriptors::mpeg4_audio::Mpeg4AudioDescriptor,
256 Iod = 0x1D => crate::descriptors::iod::IodDescriptor<'a>,
257 Sl = 0x1E => crate::descriptors::sl::SlDescriptor,
258 Fmc = 0x1F => crate::descriptors::fmc::FmcDescriptor,
259 ExternalEsId = 0x20 => crate::descriptors::external_es_id::ExternalEsIdDescriptor,
260 Muxcode = 0x21 => crate::descriptors::muxcode::MuxcodeDescriptor<'a>,
261 FmxBufferSize = 0x22 => crate::descriptors::fmx_buffer_size::FmxBufferSizeDescriptor<'a>,
262 MultiplexBuffer = 0x23 => crate::descriptors::multiplex_buffer::MultiplexBufferDescriptor,
263 ContentLabeling = 0x24 => crate::descriptors::content_labeling::ContentLabelingDescriptor<'a>,
264 MetadataPointer = 0x25 => crate::descriptors::metadata_pointer::MetadataPointerDescriptor<'a>,
265 Metadata = 0x26 => crate::descriptors::metadata::MetadataDescriptor<'a>,
266 MetadataStd = 0x27 => crate::descriptors::metadata_std::MetadataStdDescriptor,
267 AvcVideo = 0x28 => crate::descriptors::avc_video::AvcVideoDescriptor,
268 AvcTimingAndHrd = 0x2A => crate::descriptors::avc_timing_and_hrd::AvcTimingAndHrdDescriptor,
269 Mpeg2AacAudio = 0x2B => crate::descriptors::mpeg2_aac_audio::Mpeg2AacAudioDescriptor,
270 FlexMuxTiming = 0x2C => crate::descriptors::flex_mux_timing::FlexMuxTimingDescriptor,
271 Mpeg4Text = 0x2D => crate::descriptors::mpeg4_text::Mpeg4TextDescriptor<'a>,
272 Mpeg4AudioExtension = 0x2E => crate::descriptors::mpeg4_audio_extension::Mpeg4AudioExtensionDescriptor,
273 AuxiliaryVideoStream = 0x2F => crate::descriptors::auxiliary_video_stream::AuxiliaryVideoStreamDescriptor<'a>,
274 SvcExtension = 0x30 => crate::descriptors::svc_extension::SvcExtensionDescriptor,
275 MvcExtension = 0x31 => crate::descriptors::mvc_extension::MvcExtensionDescriptor,
276 J2kVideo = 0x32 => crate::descriptors::j2k_video::J2kVideoDescriptor<'a>,
277 MvcOperationPoint = 0x33 => crate::descriptors::mvc_operation_point::MvcOperationPointDescriptor,
278 Mpeg2StereoscopicVideoFormat = 0x34 => crate::descriptors::stereoscopic::Mpeg2StereoscopicVideoFormatDescriptor,
279 StereoscopicProgramInfo = 0x35 => crate::descriptors::stereoscopic::StereoscopicProgramInfoDescriptor,
280 StereoscopicVideoInfo = 0x36 => crate::descriptors::stereoscopic::StereoscopicVideoInfoDescriptor,
281 TransportProfile = 0x37 => crate::descriptors::transport_profile::TransportProfileDescriptor,
282 HevcVideo = 0x38 => crate::descriptors::hevc_video::HevcVideoDescriptor,
283 VvcVideo = 0x39 => crate::descriptors::vvc_video::VvcVideoDescriptor,
284 EvcVideo = 0x3A => crate::descriptors::evc_video::EvcVideoDescriptor,
285 Mpeg2Extension = 0x3F => crate::descriptors::mpeg2_extension::Mpeg2ExtensionDescriptor,
286 NetworkName = 0x40 => crate::descriptors::network_name::NetworkNameDescriptor<'a>,
288 ServiceList = 0x41 => crate::descriptors::service_list::ServiceListDescriptor,
289 Stuffing = 0x42 => crate::descriptors::stuffing::StuffingDescriptor<'a>,
290 SatelliteDeliverySystem = 0x43 => crate::descriptors::satellite_delivery_system::SatelliteDeliverySystemDescriptor,
291 CableDeliverySystem = 0x44 => crate::descriptors::cable_delivery_system::CableDeliverySystemDescriptor,
292 VbiData = 0x45 => crate::descriptors::vbi_data::VbiDataDescriptor<'a>,
293 VbiTeletext = 0x46 => crate::descriptors::vbi_teletext::VbiTeletextDescriptor,
294 BouquetName = 0x47 => crate::descriptors::bouquet_name::BouquetNameDescriptor<'a>,
295 Service = 0x48 => crate::descriptors::service::ServiceDescriptor<'a>,
296 CountryAvailability = 0x49 => crate::descriptors::country_availability::CountryAvailabilityDescriptor,
297 Linkage = 0x4A => crate::descriptors::linkage::LinkageDescriptor<'a>,
298 NvodReference = 0x4B => crate::descriptors::nvod_reference::NvodReferenceDescriptor,
299 TimeShiftedService = 0x4C => crate::descriptors::time_shifted_service::TimeShiftedServiceDescriptor,
300 ShortEvent = 0x4D => crate::descriptors::short_event::ShortEventDescriptor<'a>,
301 ExtendedEvent = 0x4E => crate::descriptors::extended_event::ExtendedEventDescriptor<'a>,
302 TimeShiftedEvent = 0x4F => crate::descriptors::time_shifted_event::TimeShiftedEventDescriptor,
303 Component = 0x50 => crate::descriptors::component::ComponentDescriptor<'a>,
304 Mosaic = 0x51 => crate::descriptors::mosaic::MosaicDescriptor,
305 StreamIdentifier = 0x52 => crate::descriptors::stream_identifier::StreamIdentifierDescriptor,
306 CaIdentifier = 0x53 => crate::descriptors::ca_identifier::CaIdentifierDescriptor,
307 Content = 0x54 => crate::descriptors::content::ContentDescriptor,
308 ParentalRating = 0x55 => crate::descriptors::parental_rating::ParentalRatingDescriptor,
309 Teletext = 0x56 => crate::descriptors::teletext::TeletextDescriptor,
310 Telephone = 0x57 => crate::descriptors::telephone::TelephoneDescriptor<'a>,
311 LocalTimeOffset = 0x58 => crate::descriptors::local_time_offset::LocalTimeOffsetDescriptor,
312 Subtitling = 0x59 => crate::descriptors::subtitling::SubtitlingDescriptor,
313 TerrestrialDeliverySystem = 0x5A => crate::descriptors::terrestrial_delivery_system::TerrestrialDeliverySystemDescriptor,
314 MultilingualNetworkName = 0x5B => crate::descriptors::multilingual_network_name::MultilingualNetworkNameDescriptor<'a>,
315 MultilingualBouquetName = 0x5C => crate::descriptors::multilingual_bouquet_name::MultilingualBouquetNameDescriptor<'a>,
316 MultilingualServiceName = 0x5D => crate::descriptors::multilingual_service_name::MultilingualServiceNameDescriptor<'a>,
317 MultilingualComponent = 0x5E => crate::descriptors::multilingual_component::MultilingualComponentDescriptor<'a>,
318 PrivateDataSpecifier = 0x5F => crate::descriptors::private_data_specifier::PrivateDataSpecifierDescriptor,
319 ServiceMove = 0x60 => crate::descriptors::service_move::ServiceMoveDescriptor,
320 ShortSmoothingBuffer = 0x61 => crate::descriptors::short_smoothing_buffer::ShortSmoothingBufferDescriptor<'a>,
321 FrequencyList = 0x62 => crate::descriptors::frequency_list::FrequencyListDescriptor,
322 PartialTransportStream = 0x63 => crate::descriptors::partial_transport_stream::PartialTransportStreamDescriptor,
323 DataBroadcast = 0x64 => crate::descriptors::data_broadcast::DataBroadcastDescriptor<'a>,
324 Scrambling = 0x65 => crate::descriptors::scrambling::ScramblingDescriptor,
325 DataBroadcastId = 0x66 => crate::descriptors::data_broadcast_id::DataBroadcastIdDescriptor<'a>,
326 TransportStream = 0x67 => crate::descriptors::transport_stream::TransportStreamDescriptor<'a>,
327 Dsng = 0x68 => crate::descriptors::dsng::DsngDescriptor<'a>,
328 Pdc = 0x69 => crate::descriptors::pdc::PdcDescriptor,
329 Ac3 = 0x6A => crate::descriptors::ac3::Ac3Descriptor<'a>,
330 AncillaryData = 0x6B => crate::descriptors::ancillary_data::AncillaryDataDescriptor,
331 CellList = 0x6C => crate::descriptors::cell_list::CellListDescriptor,
332 CellFrequencyLink = 0x6D => crate::descriptors::cell_frequency_link::CellFrequencyLinkDescriptor,
333 AnnouncementSupport = 0x6E => crate::descriptors::announcement_support::AnnouncementSupportDescriptor,
334 ApplicationSignalling = 0x6F => crate::descriptors::application_signalling::ApplicationSignallingDescriptor,
335 AdaptationFieldData = 0x70 => crate::descriptors::adaptation_field_data::AdaptationFieldDataDescriptor,
336 ServiceIdentifier = 0x71 => crate::descriptors::service_identifier::ServiceIdentifierDescriptor<'a>,
337 ServiceAvailability = 0x72 => crate::descriptors::service_availability::ServiceAvailabilityDescriptor,
338 DefaultAuthority = 0x73 => crate::descriptors::default_authority::DefaultAuthorityDescriptor<'a>,
339 RelatedContent = 0x74 => crate::descriptors::related_content::RelatedContentDescriptor,
340 TvaId = 0x75 => crate::descriptors::tva_id::TvaIdDescriptor,
341 ContentIdentifier = 0x76 => crate::descriptors::content_identifier::ContentIdentifierDescriptor<'a>,
342 TimeSliceFecIdentifier = 0x77 => crate::descriptors::time_slice_fec_identifier::TimeSliceFecIdentifierDescriptor<'a>,
343 EcmRepetitionRate = 0x78 => crate::descriptors::ecm_repetition_rate::EcmRepetitionRateDescriptor<'a>,
344 S2SatelliteDeliverySystem = 0x79 => crate::descriptors::s2_satellite_delivery_system::S2SatelliteDeliverySystemDescriptor,
345 EnhancedAc3 = 0x7A => crate::descriptors::enhanced_ac3::EnhancedAc3Descriptor<'a>,
346 Dts = 0x7B => crate::descriptors::dts::DtsDescriptor<'a>,
347 Aac = 0x7C => crate::descriptors::aac::AacDescriptor<'a>,
348 XaitLocation = 0x7D => crate::descriptors::xait_location::XaitLocationDescriptor,
349 FtaContentManagement = 0x7E => crate::descriptors::fta_content_management::FtaContentManagementDescriptor,
350 Extension = 0x7F => crate::descriptors::extension::ExtensionDescriptor<'a>;
351 @no_dispatch
355 TuwaAvs3Video => crate::descriptors::tuwa_avs3_video::TuwaAvs3VideoDescriptor,
357 Avs2Video => crate::descriptors::avs2_video::Avs2VideoDescriptor,
358 LogicalChannel => crate::descriptors::logical_channel::LogicalChannelDescriptor,
359 NordigLogicalChannelV1 => crate::descriptors::nordig::NordigLogicalChannelV1,
360 ChinaCaptionService => crate::descriptors::china_caption_service::ChinaCaptionServiceDescriptor,
361 AtscCaptionService => crate::descriptors::atsc_caption_service::AtscCaptionServiceDescriptor,
362 NordigLogicalChannelV2 => crate::descriptors::nordig::NordigLogicalChannelV2,
363 Avs3Video => crate::descriptors::avs3_video::Avs3VideoDescriptor,
364 Avs3Audio => crate::descriptors::avs3_audio::Avs3AudioDescriptor,
365 Avs2Audio => crate::descriptors::avs2_audio::Avs2AudioDescriptor,
366}
367
368#[must_use]
375pub fn parse_loop(bytes: &[u8]) -> DescriptorIter<'_> {
376 DescriptorIter {
377 bytes,
378 pos: 0,
379 fused: false,
380 }
381}
382
383pub(crate) fn next_loop_entry<'a>(
391 bytes: &'a [u8],
392 pos: &mut usize,
393 fused: &mut bool,
394) -> Option<crate::Result<(u8, &'a [u8])>> {
395 if *fused || *pos >= bytes.len() {
396 return None;
397 }
398 let rem = &bytes[*pos..];
399 if rem.len() < 2 {
400 *fused = true;
401 return Some(Err(crate::Error::BufferTooShort(
402 broadcast_common::error::BufferTooShort {
403 need: 2,
404 have: rem.len(),
405 what: "descriptor header in loop",
406 },
407 )));
408 }
409 let tag = rem[0];
410 let len = rem[1] as usize;
411 let total = 2 + len;
412 if rem.len() < total {
413 *fused = true;
414 return Some(Err(crate::Error::BufferTooShort(
415 broadcast_common::error::BufferTooShort {
416 need: total,
417 have: rem.len(),
418 what: "descriptor body in loop",
419 },
420 )));
421 }
422 let full = &rem[..total];
423 *pos += total;
424 Some(Ok((tag, full)))
425}
426
427#[derive(Debug, Clone)]
429pub struct DescriptorIter<'a> {
430 bytes: &'a [u8],
431 pos: usize,
432 fused: bool,
433}
434
435impl<'a> Iterator for DescriptorIter<'a> {
436 type Item = crate::Result<AnyDescriptor<'a>>;
437
438 fn next(&mut self) -> Option<Self::Item> {
439 let (tag, full) = match next_loop_entry(self.bytes, &mut self.pos, &mut self.fused)? {
440 Ok(v) => v,
441 Err(e) => return Some(Err(e)),
442 };
443 Some(match AnyDescriptor::dispatch(tag, full) {
444 Some(res) => res,
445 None => Ok(AnyDescriptor::Unknown {
446 tag,
447 body: &full[2..],
448 }),
449 })
450 }
451}
452
453impl core::iter::FusedIterator for DescriptorIter<'_> {}
454
455#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
479#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
480pub struct DescriptorLoop<'a>(&'a [u8]);
481
482const DESC_HEADER_LEN: usize = 2;
484
485impl<'a> DescriptorLoop<'a> {
486 #[must_use]
489 pub const fn new(raw: &'a [u8]) -> Self {
490 Self(raw)
491 }
492
493 #[must_use]
496 pub const fn raw(&self) -> &'a [u8] {
497 self.0
498 }
499
500 #[must_use]
504 pub fn iter(&self) -> DescriptorIter<'a> {
505 parse_loop(self.0)
506 }
507
508 pub fn raw_tags(&self) -> impl Iterator<Item = (u8, &'a [u8])> {
518 let b = self.0;
519 let mut pos = 0usize;
520 core::iter::from_fn(move || {
521 if pos + DESC_HEADER_LEN > b.len() {
522 return None;
523 }
524 let tag = b[pos];
525 let len = b[pos + 1] as usize;
526 let end = pos + DESC_HEADER_LEN + len;
527 if end > b.len() {
528 return None; }
530 let body = &b[pos + DESC_HEADER_LEN..end];
531 pos = end;
532 Some((tag, body))
533 })
534 }
535
536 #[must_use]
541 pub fn contains_tag(&self, tag: u8) -> bool {
542 let b = self.0;
543 let mut pos = 0usize;
544 while pos < b.len() {
545 if b[pos] == tag {
546 return true;
547 }
548 if pos + 1 >= b.len() {
551 break;
552 }
553 pos += DESC_HEADER_LEN + b[pos + 1] as usize;
554 }
555 false
556 }
557
558 #[must_use]
566 pub fn iter_with<'r>(
567 &self,
568 registry: &'r crate::descriptors::registry::DescriptorRegistry,
569 ) -> crate::descriptors::registry::RegistryIter<'r, 'a> {
570 registry.parse_loop(self.0)
571 }
572
573 #[must_use]
586 pub fn iter_with_extensions<'r>(
587 &self,
588 desc_reg: &'r crate::descriptors::registry::DescriptorRegistry,
589 ext_reg: &'r crate::descriptors::extension::registry::ExtensionRegistry,
590 ) -> crate::descriptors::registry::ExtRegistryIter<'r, 'a> {
591 crate::descriptors::registry::ExtRegistryIter::new(desc_reg, ext_reg, self.0, None)
592 }
593
594 #[must_use]
600 pub fn iter_with_extensions_in_scope<'r>(
601 &self,
602 desc_reg: &'r crate::descriptors::registry::DescriptorRegistry,
603 ext_reg: &'r crate::descriptors::extension::registry::ExtensionRegistry,
604 inherited: Option<u32>,
605 ) -> crate::descriptors::registry::ExtRegistryIter<'r, 'a> {
606 crate::descriptors::registry::ExtRegistryIter::new(desc_reg, ext_reg, self.0, inherited)
607 }
608}
609
610impl core::ops::Deref for DescriptorLoop<'_> {
611 type Target = [u8];
615 fn deref(&self) -> &[u8] {
616 self.0
617 }
618}
619
620impl<'a> From<&'a [u8]> for DescriptorLoop<'a> {
621 fn from(raw: &'a [u8]) -> Self {
622 Self(raw)
623 }
624}
625
626impl core::fmt::Debug for DescriptorLoop<'_> {
627 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
629 write!(f, "DescriptorLoop(<{} bytes>)", self.0.len())
630 }
631}
632
633impl<'a> IntoIterator for &DescriptorLoop<'a> {
634 type Item = crate::Result<AnyDescriptor<'a>>;
635 type IntoIter = DescriptorIter<'a>;
636 fn into_iter(self) -> Self::IntoIter {
637 self.iter()
638 }
639}
640
641#[cfg(feature = "serde")]
642impl serde::Serialize for DescriptorLoop<'_> {
643 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
648 struct Entry<'a>(crate::Result<AnyDescriptor<'a>>);
649 impl serde::Serialize for Entry<'_> {
650 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
651 match &self.0 {
652 Ok(d) => d.serialize(s),
653 Err(e) => {
654 use serde::ser::SerializeMap;
655 let mut m = s.serialize_map(Some(1))?;
656 m.serialize_entry("parseError", &e.to_string())?;
657 m.end()
658 }
659 }
660 }
661 }
662 s.collect_seq(self.iter().map(Entry))
663 }
664}
665#[cfg(test)]
671mod tests {
672 use super::*;
673
674 #[test]
675 fn contains_tag_detects_present_tag_regardless_of_body() {
676 assert!(DescriptorLoop::new(&[0x6A, 0x01, 0x80]).contains_tag(0x6A));
678 assert!(DescriptorLoop::new(&[0x6A, 0x00]).contains_tag(0x6A));
680 assert!(DescriptorLoop::new(&[0x6A, 0x01]).contains_tag(0x6A));
682 assert!(DescriptorLoop::new(&[0x09, 0x02, 0x00, 0x00, 0x7A, 0x00]).contains_tag(0x7A));
684 assert!(!DescriptorLoop::new(&[0x09, 0x02, 0x00, 0x00]).contains_tag(0x6A));
686 assert!(!DescriptorLoop::new(&[]).contains_tag(0x6A));
687 }
688
689 #[test]
690 fn raw_tags_walks_tlv_without_typed_parsing() {
691 let loop_ = DescriptorLoop::new(&[0x6A, 0x00, 0x40, 0x02, 0xAA, 0xBB]);
693 let pairs: Vec<_> = loop_.raw_tags().collect();
694 assert_eq!(pairs, vec![(0x6A, &[][..]), (0x40, &[0xAA, 0xBB][..])]);
695 let trunc = DescriptorLoop::new(&[0x40, 0x01, 0xAA, 0x6A, 0x05]);
698 let tags: Vec<u8> = trunc.raw_tags().map(|(t, _)| t).collect();
699 assert_eq!(tags, vec![0x40]); assert!(trunc.contains_tag(0x6A));
702 }
703
704 #[test]
705 fn unknown_tag_yields_unknown_with_body_sans_header() {
706 let bytes = [0xA7, 0x02, 0xDE, 0xAD];
708 let items: Vec<_> = parse_loop(&bytes).collect();
709 assert_eq!(items.len(), 1);
710 match items[0].as_ref().unwrap() {
711 AnyDescriptor::Unknown { tag, body } => {
712 assert_eq!(*tag, 0xA7);
713 assert_eq!(*body, &[0xDE, 0xAD]);
714 }
715 other => panic!("expected Unknown, got {other:?}"),
716 }
717 }
718
719 #[test]
720 fn empty_loop_yields_nothing() {
721 assert_eq!(parse_loop(&[]).count(), 0);
722 }
723
724 #[test]
725 fn logical_channel_0x83_is_not_dispatched() {
726 let bytes = [0x83, 0x04, 0x00, 0x01, 0xFC, 0x01];
728 let items: Vec<_> = parse_loop(&bytes).collect();
729 assert_eq!(items.len(), 1);
730 assert!(matches!(
731 items[0].as_ref().unwrap(),
732 AnyDescriptor::Unknown { tag: 0x83, .. }
733 ));
734 }
735
736 #[test]
737 fn descriptor_loop_iter_matches_parse_loop() {
738 let raw = [
739 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, 0xA7, 0x02, 0xCA, 0xFE, ];
742 let via_loop: Vec<_> = DescriptorLoop::new(&raw)
743 .iter()
744 .map(|r| format!("{r:?}"))
745 .collect();
746 let via_fn: Vec<_> = parse_loop(&raw).map(|r| format!("{r:?}")).collect();
747 assert_eq!(via_loop, via_fn);
748 assert_eq!(DescriptorLoop::new(&raw).raw(), &raw[..]);
750 assert_eq!(DescriptorLoop::new(&raw).len(), raw.len());
751 let count = (&DescriptorLoop::new(&raw)).into_iter().count();
753 assert_eq!(count, 2);
754 }
755
756 #[test]
757 fn descriptor_loop_debug_is_cheap() {
758 let raw = [0x4D, 0x02, 0x01, 0x02];
759 assert_eq!(
760 format!("{:?}", DescriptorLoop::new(&raw)),
761 "DescriptorLoop(<4 bytes>)"
762 );
763 }
764
765 #[test]
766 fn iter_with_custom_tag_yields_other() {
767 use crate::descriptors::registry::DescriptorRegistry;
768 use crate::traits::DescriptorDef;
769
770 #[derive(Debug, PartialEq, Eq)]
771 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
772 struct MyTag0xA7 {
773 x: u8,
774 }
775
776 impl<'a> broadcast_common::Parse<'a> for MyTag0xA7 {
777 type Error = crate::error::Error;
778 fn parse(bytes: &'a [u8]) -> crate::Result<Self> {
779 if bytes.len() < 3 {
780 return Err(crate::error::Error::BufferTooShort(
781 broadcast_common::error::BufferTooShort {
782 need: 3,
783 have: bytes.len(),
784 what: "MyTag0xA7",
785 },
786 ));
787 }
788 Ok(Self { x: bytes[2] })
789 }
790 }
791
792 impl DescriptorDef<'_> for MyTag0xA7 {
793 const TAG: u8 = 0xA7;
794 const NAME: &'static str = "MY_TAG_0xA7";
795 }
796
797 let mut reg = DescriptorRegistry::new();
798 reg.register::<MyTag0xA7>();
799
800 let raw = [
801 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, 0xA7, 0x02, 0xCA, 0xFE,
802 ];
803 let loop_ = DescriptorLoop::new(&raw);
804 let items: Vec<_> = loop_.iter_with(®).collect::<Result<_, _>>().unwrap();
805 assert_eq!(items.len(), 2);
806 assert!(matches!(items[0], AnyDescriptor::ShortEvent(_)));
807 match &items[1] {
808 AnyDescriptor::Other { tag, value } => {
809 assert_eq!(*tag, 0xA7);
810 assert_eq!(value.downcast_ref::<MyTag0xA7>().unwrap().x, 0xCA);
811 }
812 other => panic!("expected Other, got {other:?}"),
813 }
814 }
815
816 #[test]
817 fn iter_with_empty_registry_matches_iter_for_builtin() {
818 let raw = [
819 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, 0xA7, 0x02, 0xCA, 0xFE,
820 ];
821 let loop_ = DescriptorLoop::new(&raw);
822 let reg = crate::descriptors::registry::DescriptorRegistry::new();
823 let via_iter: Vec<_> = loop_.iter().collect();
824 let via_iter_with: Vec<_> = loop_.iter_with(®).collect();
825
826 assert_eq!(via_iter.len(), via_iter_with.len());
827 for (a, b) in via_iter.iter().zip(via_iter_with.iter()) {
828 match (a, b) {
829 (Ok(AnyDescriptor::ShortEvent(_)), Ok(AnyDescriptor::ShortEvent(_))) => {}
830 (
831 Ok(AnyDescriptor::Unknown { tag: t1, body: b1 }),
832 Ok(AnyDescriptor::Unknown { tag: t2, body: b2 }),
833 ) => {
834 assert_eq!(t1, t2);
835 assert_eq!(b1, b2);
836 }
837 (Err(_), Err(_)) => {}
838 (l, r) => panic!("mismatch: {l:?} vs {r:?}"),
839 }
840 }
841 }
842
843 #[cfg(feature = "serde")]
844 #[test]
845 fn descriptor_loop_serializes_typed_unknown_and_parse_error() {
846 let raw = [
850 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, 0xA7, 0x02, 0xCA, 0xFE, 0x55, 0x05, 0x00, ];
854 let v = serde_json::to_value(DescriptorLoop::new(&raw)).unwrap();
855 let arr = v.as_array().expect("sequence");
856 assert_eq!(arr.len(), 3);
857 assert!(arr[0].get("shortEvent").is_some(), "got {}", arr[0]);
859 assert_eq!(arr[0]["shortEvent"]["event_name"], "Hi");
860 let unknown = arr[1].get("unknown").expect("unknown variant");
862 assert_eq!(unknown["tag"], 0xA7);
863 assert_eq!(unknown["body"], serde_json::json!([0xCA, 0xFE]));
864 assert!(
866 arr[2].get("parseError").is_some(),
867 "expected parseError, got {}",
868 arr[2]
869 );
870 }
871}