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 pub(crate) fn dispatch(tag: u8, full: &$lt [u8]) -> Option<crate::Result<Self>> {
162 use dvb_common::Parse;
163 match tag {
164 $(
165 $tag => Some(<$($path)::+>::parse(full).map(Self::$variant)),
166 )+
167 _ => None,
168 }
169 }
170 }
171
172 #[cfg(test)]
173 mod macro_drift {
174 #[test]
175 fn tag_literals_match_descriptor_def() {
176 use crate::traits::DescriptorDef;
177 $(
178 assert_eq!(
179 $tag,
180 <$($path)::+ as DescriptorDef>::TAG,
181 concat!("tag literal drift for ", stringify!($variant)),
182 );
183 assert!(
184 !<$($path)::+ as DescriptorDef>::NAME.is_empty(),
185 concat!("empty NAME for ", stringify!($variant)),
186 );
187 )+
188 $($(
189 assert!(
190 !<$($nd_path)::+ as DescriptorDef>::NAME.is_empty(),
191 concat!("empty NAME for ", stringify!($nd_variant)),
192 );
193 )+)?
194 }
195 }
196 };
197}
198
199declare_descriptors! {'a;
200 Registration = 0x05 => crate::descriptors::registration::RegistrationDescriptor<'a>,
202 DataStreamAlignment = 0x06 => crate::descriptors::data_stream_alignment::DataStreamAlignmentDescriptor,
203 Ca = 0x09 => crate::descriptors::ca::CaDescriptor<'a>,
204 Iso639Language = 0x0A => crate::descriptors::iso_639_language::Iso639LanguageDescriptor,
205 PrivateDataIndicator = 0x0F => crate::descriptors::private_data_indicator::PrivateDataIndicatorDescriptor,
206 CarouselIdentifier = 0x13 => crate::descriptors::carousel_identifier::CarouselIdentifierDescriptor<'a>,
207 NetworkName = 0x40 => crate::descriptors::network_name::NetworkNameDescriptor<'a>,
209 ServiceList = 0x41 => crate::descriptors::service_list::ServiceListDescriptor,
210 Stuffing = 0x42 => crate::descriptors::stuffing::StuffingDescriptor<'a>,
211 SatelliteDeliverySystem = 0x43 => crate::descriptors::satellite_delivery_system::SatelliteDeliverySystemDescriptor,
212 CableDeliverySystem = 0x44 => crate::descriptors::cable_delivery_system::CableDeliverySystemDescriptor,
213 VbiData = 0x45 => crate::descriptors::vbi_data::VbiDataDescriptor<'a>,
214 VbiTeletext = 0x46 => crate::descriptors::vbi_teletext::VbiTeletextDescriptor,
215 BouquetName = 0x47 => crate::descriptors::bouquet_name::BouquetNameDescriptor<'a>,
216 Service = 0x48 => crate::descriptors::service::ServiceDescriptor<'a>,
217 CountryAvailability = 0x49 => crate::descriptors::country_availability::CountryAvailabilityDescriptor,
218 Linkage = 0x4A => crate::descriptors::linkage::LinkageDescriptor<'a>,
219 NvodReference = 0x4B => crate::descriptors::nvod_reference::NvodReferenceDescriptor,
220 TimeShiftedService = 0x4C => crate::descriptors::time_shifted_service::TimeShiftedServiceDescriptor,
221 ShortEvent = 0x4D => crate::descriptors::short_event::ShortEventDescriptor<'a>,
222 ExtendedEvent = 0x4E => crate::descriptors::extended_event::ExtendedEventDescriptor<'a>,
223 TimeShiftedEvent = 0x4F => crate::descriptors::time_shifted_event::TimeShiftedEventDescriptor,
224 Component = 0x50 => crate::descriptors::component::ComponentDescriptor<'a>,
225 Mosaic = 0x51 => crate::descriptors::mosaic::MosaicDescriptor,
226 StreamIdentifier = 0x52 => crate::descriptors::stream_identifier::StreamIdentifierDescriptor,
227 CaIdentifier = 0x53 => crate::descriptors::ca_identifier::CaIdentifierDescriptor,
228 Content = 0x54 => crate::descriptors::content::ContentDescriptor,
229 ParentalRating = 0x55 => crate::descriptors::parental_rating::ParentalRatingDescriptor,
230 Teletext = 0x56 => crate::descriptors::teletext::TeletextDescriptor,
231 Telephone = 0x57 => crate::descriptors::telephone::TelephoneDescriptor<'a>,
232 LocalTimeOffset = 0x58 => crate::descriptors::local_time_offset::LocalTimeOffsetDescriptor,
233 Subtitling = 0x59 => crate::descriptors::subtitling::SubtitlingDescriptor,
234 TerrestrialDeliverySystem = 0x5A => crate::descriptors::terrestrial_delivery_system::TerrestrialDeliverySystemDescriptor,
235 MultilingualNetworkName = 0x5B => crate::descriptors::multilingual_network_name::MultilingualNetworkNameDescriptor<'a>,
236 MultilingualBouquetName = 0x5C => crate::descriptors::multilingual_bouquet_name::MultilingualBouquetNameDescriptor<'a>,
237 MultilingualServiceName = 0x5D => crate::descriptors::multilingual_service_name::MultilingualServiceNameDescriptor<'a>,
238 MultilingualComponent = 0x5E => crate::descriptors::multilingual_component::MultilingualComponentDescriptor<'a>,
239 PrivateDataSpecifier = 0x5F => crate::descriptors::private_data_specifier::PrivateDataSpecifierDescriptor,
240 ServiceMove = 0x60 => crate::descriptors::service_move::ServiceMoveDescriptor,
241 ShortSmoothingBuffer = 0x61 => crate::descriptors::short_smoothing_buffer::ShortSmoothingBufferDescriptor<'a>,
242 FrequencyList = 0x62 => crate::descriptors::frequency_list::FrequencyListDescriptor,
243 PartialTransportStream = 0x63 => crate::descriptors::partial_transport_stream::PartialTransportStreamDescriptor,
244 DataBroadcast = 0x64 => crate::descriptors::data_broadcast::DataBroadcastDescriptor<'a>,
245 Scrambling = 0x65 => crate::descriptors::scrambling::ScramblingDescriptor,
246 DataBroadcastId = 0x66 => crate::descriptors::data_broadcast_id::DataBroadcastIdDescriptor<'a>,
247 TransportStream = 0x67 => crate::descriptors::transport_stream::TransportStreamDescriptor<'a>,
248 Dsng = 0x68 => crate::descriptors::dsng::DsngDescriptor<'a>,
249 Pdc = 0x69 => crate::descriptors::pdc::PdcDescriptor,
250 Ac3 = 0x6A => crate::descriptors::ac3::Ac3Descriptor<'a>,
251 AncillaryData = 0x6B => crate::descriptors::ancillary_data::AncillaryDataDescriptor,
252 CellList = 0x6C => crate::descriptors::cell_list::CellListDescriptor,
253 CellFrequencyLink = 0x6D => crate::descriptors::cell_frequency_link::CellFrequencyLinkDescriptor,
254 AnnouncementSupport = 0x6E => crate::descriptors::announcement_support::AnnouncementSupportDescriptor,
255 ApplicationSignalling = 0x6F => crate::descriptors::application_signalling::ApplicationSignallingDescriptor,
256 AdaptationFieldData = 0x70 => crate::descriptors::adaptation_field_data::AdaptationFieldDataDescriptor,
257 ServiceIdentifier = 0x71 => crate::descriptors::service_identifier::ServiceIdentifierDescriptor<'a>,
258 ServiceAvailability = 0x72 => crate::descriptors::service_availability::ServiceAvailabilityDescriptor,
259 DefaultAuthority = 0x73 => crate::descriptors::default_authority::DefaultAuthorityDescriptor<'a>,
260 RelatedContent = 0x74 => crate::descriptors::related_content::RelatedContentDescriptor,
261 TvaId = 0x75 => crate::descriptors::tva_id::TvaIdDescriptor,
262 ContentIdentifier = 0x76 => crate::descriptors::content_identifier::ContentIdentifierDescriptor<'a>,
263 TimeSliceFecIdentifier = 0x77 => crate::descriptors::time_slice_fec_identifier::TimeSliceFecIdentifierDescriptor<'a>,
264 EcmRepetitionRate = 0x78 => crate::descriptors::ecm_repetition_rate::EcmRepetitionRateDescriptor<'a>,
265 S2SatelliteDeliverySystem = 0x79 => crate::descriptors::s2_satellite_delivery_system::S2SatelliteDeliverySystemDescriptor,
266 EnhancedAc3 = 0x7A => crate::descriptors::enhanced_ac3::EnhancedAc3Descriptor<'a>,
267 Dts = 0x7B => crate::descriptors::dts::DtsDescriptor<'a>,
268 Aac = 0x7C => crate::descriptors::aac::AacDescriptor<'a>,
269 XaitLocation = 0x7D => crate::descriptors::xait_location::XaitLocationDescriptor,
270 FtaContentManagement = 0x7E => crate::descriptors::fta_content_management::FtaContentManagementDescriptor,
271 Extension = 0x7F => crate::descriptors::extension::ExtensionDescriptor<'a>;
272 @no_dispatch
276 LogicalChannel => crate::descriptors::logical_channel::LogicalChannelDescriptor,
277}
278
279#[must_use]
286pub fn parse_loop(bytes: &[u8]) -> DescriptorIter<'_> {
287 DescriptorIter {
288 bytes,
289 pos: 0,
290 fused: false,
291 }
292}
293
294pub(crate) fn next_loop_entry<'a>(
302 bytes: &'a [u8],
303 pos: &mut usize,
304 fused: &mut bool,
305) -> Option<crate::Result<(u8, &'a [u8])>> {
306 if *fused || *pos >= bytes.len() {
307 return None;
308 }
309 let rem = &bytes[*pos..];
310 if rem.len() < 2 {
311 *fused = true;
312 return Some(Err(crate::Error::BufferTooShort {
313 need: 2,
314 have: rem.len(),
315 what: "descriptor header in loop",
316 }));
317 }
318 let tag = rem[0];
319 let len = rem[1] as usize;
320 let total = 2 + len;
321 if rem.len() < total {
322 *fused = true;
323 return Some(Err(crate::Error::BufferTooShort {
324 need: total,
325 have: rem.len(),
326 what: "descriptor body in loop",
327 }));
328 }
329 let full = &rem[..total];
330 *pos += total;
331 Some(Ok((tag, full)))
332}
333
334#[derive(Debug, Clone)]
336pub struct DescriptorIter<'a> {
337 bytes: &'a [u8],
338 pos: usize,
339 fused: bool,
340}
341
342impl<'a> Iterator for DescriptorIter<'a> {
343 type Item = crate::Result<AnyDescriptor<'a>>;
344
345 fn next(&mut self) -> Option<Self::Item> {
346 let (tag, full) = match next_loop_entry(self.bytes, &mut self.pos, &mut self.fused)? {
347 Ok(v) => v,
348 Err(e) => return Some(Err(e)),
349 };
350 Some(match AnyDescriptor::dispatch(tag, full) {
351 Some(res) => res,
352 None => Ok(AnyDescriptor::Unknown {
353 tag,
354 body: &full[2..],
355 }),
356 })
357 }
358}
359
360impl core::iter::FusedIterator for DescriptorIter<'_> {}
361
362#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
386#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
387pub struct DescriptorLoop<'a>(&'a [u8]);
388
389const DESC_HEADER_LEN: usize = 2;
391
392impl<'a> DescriptorLoop<'a> {
393 #[must_use]
396 pub const fn new(raw: &'a [u8]) -> Self {
397 Self(raw)
398 }
399
400 #[must_use]
403 pub const fn raw(&self) -> &'a [u8] {
404 self.0
405 }
406
407 #[must_use]
411 pub fn iter(&self) -> DescriptorIter<'a> {
412 parse_loop(self.0)
413 }
414
415 pub fn raw_tags(&self) -> impl Iterator<Item = (u8, &'a [u8])> {
425 let b = self.0;
426 let mut pos = 0usize;
427 core::iter::from_fn(move || {
428 if pos + DESC_HEADER_LEN > b.len() {
429 return None;
430 }
431 let tag = b[pos];
432 let len = b[pos + 1] as usize;
433 let end = pos + DESC_HEADER_LEN + len;
434 if end > b.len() {
435 return None; }
437 let body = &b[pos + DESC_HEADER_LEN..end];
438 pos = end;
439 Some((tag, body))
440 })
441 }
442
443 #[must_use]
448 pub fn contains_tag(&self, tag: u8) -> bool {
449 let b = self.0;
450 let mut pos = 0usize;
451 while pos < b.len() {
452 if b[pos] == tag {
453 return true;
454 }
455 if pos + 1 >= b.len() {
458 break;
459 }
460 pos += DESC_HEADER_LEN + b[pos + 1] as usize;
461 }
462 false
463 }
464
465 #[must_use]
473 pub fn iter_with<'r>(
474 &self,
475 registry: &'r crate::descriptors::registry::DescriptorRegistry,
476 ) -> crate::descriptors::registry::RegistryIter<'r, 'a> {
477 registry.parse_loop(self.0)
478 }
479
480 #[must_use]
493 pub fn iter_with_extensions<'r>(
494 &self,
495 desc_reg: &'r crate::descriptors::registry::DescriptorRegistry,
496 ext_reg: &'r crate::descriptors::extension::registry::ExtensionRegistry,
497 ) -> crate::descriptors::registry::ExtRegistryIter<'r, 'a> {
498 crate::descriptors::registry::ExtRegistryIter::new(desc_reg, ext_reg, self.0)
499 }
500}
501
502impl<'a> core::ops::Deref for DescriptorLoop<'a> {
503 type Target = [u8];
507 fn deref(&self) -> &[u8] {
508 self.0
509 }
510}
511
512impl<'a> From<&'a [u8]> for DescriptorLoop<'a> {
513 fn from(raw: &'a [u8]) -> Self {
514 Self(raw)
515 }
516}
517
518impl core::fmt::Debug for DescriptorLoop<'_> {
519 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
521 write!(f, "DescriptorLoop(<{} bytes>)", self.0.len())
522 }
523}
524
525impl<'a> IntoIterator for &DescriptorLoop<'a> {
526 type Item = crate::Result<AnyDescriptor<'a>>;
527 type IntoIter = DescriptorIter<'a>;
528 fn into_iter(self) -> Self::IntoIter {
529 self.iter()
530 }
531}
532
533#[cfg(feature = "serde")]
534impl serde::Serialize for DescriptorLoop<'_> {
535 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
540 struct Entry<'a>(crate::Result<AnyDescriptor<'a>>);
541 impl serde::Serialize for Entry<'_> {
542 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
543 match &self.0 {
544 Ok(d) => d.serialize(s),
545 Err(e) => {
546 use serde::ser::SerializeMap;
547 let mut m = s.serialize_map(Some(1))?;
548 m.serialize_entry("parseError", &e.to_string())?;
549 m.end()
550 }
551 }
552 }
553 }
554 s.collect_seq(self.iter().map(Entry))
555 }
556}
557#[cfg(test)]
563mod tests {
564 use super::*;
565
566 #[test]
567 fn contains_tag_detects_present_tag_regardless_of_body() {
568 assert!(DescriptorLoop::new(&[0x6A, 0x01, 0x80]).contains_tag(0x6A));
570 assert!(DescriptorLoop::new(&[0x6A, 0x00]).contains_tag(0x6A));
572 assert!(DescriptorLoop::new(&[0x6A, 0x01]).contains_tag(0x6A));
574 assert!(DescriptorLoop::new(&[0x09, 0x02, 0x00, 0x00, 0x7A, 0x00]).contains_tag(0x7A));
576 assert!(!DescriptorLoop::new(&[0x09, 0x02, 0x00, 0x00]).contains_tag(0x6A));
578 assert!(!DescriptorLoop::new(&[]).contains_tag(0x6A));
579 }
580
581 #[test]
582 fn raw_tags_walks_tlv_without_typed_parsing() {
583 let loop_ = DescriptorLoop::new(&[0x6A, 0x00, 0x40, 0x02, 0xAA, 0xBB]);
585 let pairs: Vec<_> = loop_.raw_tags().collect();
586 assert_eq!(pairs, vec![(0x6A, &[][..]), (0x40, &[0xAA, 0xBB][..])]);
587 let trunc = DescriptorLoop::new(&[0x40, 0x01, 0xAA, 0x6A, 0x05]);
590 let tags: Vec<u8> = trunc.raw_tags().map(|(t, _)| t).collect();
591 assert_eq!(tags, vec![0x40]); assert!(trunc.contains_tag(0x6A));
594 }
595
596 #[test]
597 fn unknown_tag_yields_unknown_with_body_sans_header() {
598 let bytes = [0xA7, 0x02, 0xDE, 0xAD];
600 let items: Vec<_> = parse_loop(&bytes).collect();
601 assert_eq!(items.len(), 1);
602 match items[0].as_ref().unwrap() {
603 AnyDescriptor::Unknown { tag, body } => {
604 assert_eq!(*tag, 0xA7);
605 assert_eq!(*body, &[0xDE, 0xAD]);
606 }
607 other => panic!("expected Unknown, got {other:?}"),
608 }
609 }
610
611 #[test]
612 fn empty_loop_yields_nothing() {
613 assert_eq!(parse_loop(&[]).count(), 0);
614 }
615
616 #[test]
617 fn logical_channel_0x83_is_not_dispatched() {
618 let bytes = [0x83, 0x04, 0x00, 0x01, 0xFC, 0x01];
620 let items: Vec<_> = parse_loop(&bytes).collect();
621 assert_eq!(items.len(), 1);
622 assert!(matches!(
623 items[0].as_ref().unwrap(),
624 AnyDescriptor::Unknown { tag: 0x83, .. }
625 ));
626 }
627
628 #[test]
629 fn descriptor_loop_iter_matches_parse_loop() {
630 let raw = [
631 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, 0xA7, 0x02, 0xCA, 0xFE, ];
634 let via_loop: Vec<_> = DescriptorLoop::new(&raw)
635 .iter()
636 .map(|r| format!("{r:?}"))
637 .collect();
638 let via_fn: Vec<_> = parse_loop(&raw).map(|r| format!("{r:?}")).collect();
639 assert_eq!(via_loop, via_fn);
640 assert_eq!(DescriptorLoop::new(&raw).raw(), &raw[..]);
642 assert_eq!(DescriptorLoop::new(&raw).len(), raw.len());
643 let count = (&DescriptorLoop::new(&raw)).into_iter().count();
645 assert_eq!(count, 2);
646 }
647
648 #[test]
649 fn descriptor_loop_debug_is_cheap() {
650 let raw = [0x4D, 0x02, 0x01, 0x02];
651 assert_eq!(
652 format!("{:?}", DescriptorLoop::new(&raw)),
653 "DescriptorLoop(<4 bytes>)"
654 );
655 }
656
657 #[test]
658 fn iter_with_custom_tag_yields_other() {
659 use crate::descriptors::registry::DescriptorRegistry;
660 use crate::traits::DescriptorDef;
661
662 #[derive(Debug, PartialEq, Eq)]
663 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
664 struct MyTag0xA7 {
665 x: u8,
666 }
667
668 impl<'a> dvb_common::Parse<'a> for MyTag0xA7 {
669 type Error = crate::error::Error;
670 fn parse(bytes: &'a [u8]) -> crate::Result<Self> {
671 if bytes.len() < 3 {
672 return Err(crate::error::Error::BufferTooShort {
673 need: 3,
674 have: bytes.len(),
675 what: "MyTag0xA7",
676 });
677 }
678 Ok(Self { x: bytes[2] })
679 }
680 }
681
682 impl<'a> DescriptorDef<'a> for MyTag0xA7 {
683 const TAG: u8 = 0xA7;
684 const NAME: &'static str = "MY_TAG_0xA7";
685 }
686
687 let mut reg = DescriptorRegistry::new();
688 reg.register::<MyTag0xA7>();
689
690 let raw = [
691 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, 0xA7, 0x02, 0xCA, 0xFE,
692 ];
693 let loop_ = DescriptorLoop::new(&raw);
694 let items: Vec<_> = loop_.iter_with(®).collect::<Result<_, _>>().unwrap();
695 assert_eq!(items.len(), 2);
696 assert!(matches!(items[0], AnyDescriptor::ShortEvent(_)));
697 match &items[1] {
698 AnyDescriptor::Other { tag, value } => {
699 assert_eq!(*tag, 0xA7);
700 assert_eq!(value.downcast_ref::<MyTag0xA7>().unwrap().x, 0xCA);
701 }
702 other => panic!("expected Other, got {other:?}"),
703 }
704 }
705
706 #[test]
707 fn iter_with_empty_registry_matches_iter_for_builtin() {
708 let raw = [
709 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, 0xA7, 0x02, 0xCA, 0xFE,
710 ];
711 let loop_ = DescriptorLoop::new(&raw);
712 let reg = crate::descriptors::registry::DescriptorRegistry::new();
713 let via_iter: Vec<_> = loop_.iter().collect();
714 let via_iter_with: Vec<_> = loop_.iter_with(®).collect();
715
716 assert_eq!(via_iter.len(), via_iter_with.len());
717 for (a, b) in via_iter.iter().zip(via_iter_with.iter()) {
718 match (a, b) {
719 (Ok(AnyDescriptor::ShortEvent(_)), Ok(AnyDescriptor::ShortEvent(_))) => {}
720 (
721 Ok(AnyDescriptor::Unknown { tag: t1, body: b1 }),
722 Ok(AnyDescriptor::Unknown { tag: t2, body: b2 }),
723 ) => {
724 assert_eq!(t1, t2);
725 assert_eq!(b1, b2);
726 }
727 (Err(_), Err(_)) => {}
728 (l, r) => panic!("mismatch: {l:?} vs {r:?}"),
729 }
730 }
731 }
732
733 #[cfg(feature = "serde")]
734 #[test]
735 fn descriptor_loop_serializes_typed_unknown_and_parse_error() {
736 let raw = [
740 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, 0xA7, 0x02, 0xCA, 0xFE, 0x55, 0x05, 0x00, ];
744 let v = serde_json::to_value(DescriptorLoop::new(&raw)).unwrap();
745 let arr = v.as_array().expect("sequence");
746 assert_eq!(arr.len(), 3);
747 assert!(arr[0].get("shortEvent").is_some(), "got {}", arr[0]);
749 assert_eq!(arr[0]["shortEvent"]["event_name"], "Hi");
750 let unknown = arr[1].get("unknown").expect("unknown variant");
752 assert_eq!(unknown["tag"], 0xA7);
753 assert_eq!(unknown["body"], serde_json::json!([0xCA, 0xFE]));
754 assert!(
756 arr[2].get("parseError").is_some(),
757 "expected parseError, got {}",
758 arr[2]
759 );
760 }
761}