1macro_rules! declare_descriptors {
57 (
58 $lt:lifetime;
59 $( $variant:ident = $tag:literal => $($path:ident)::+ $(<$plt:lifetime>)? ),+ $(,)?
60 $( ; @no_dispatch $( $nd_variant:ident => $($nd_path:ident)::+ $(<$nd_plt:lifetime>)? ),+ $(,)? )?
61 ) => {
62 #[derive(Debug)]
69 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
70 #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
71 #[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
76 #[non_exhaustive]
77 pub enum AnyDescriptor<$lt> {
78 $(
79 #[allow(missing_docs)]
80 $variant($($path)::+ $(<$plt>)?),
81 )+
82 $($(
83 #[allow(missing_docs)]
84 $nd_variant($($nd_path)::+ $(<$nd_plt>)?),
85 )+)?
86 Other {
90 tag: u8,
92 #[cfg_attr(
96 feature = "serde",
97 serde(serialize_with = "crate::descriptors::registry::serialize_erased")
98 )]
99 value: Box<dyn crate::descriptors::registry::DescriptorObject>,
100 },
101 Unknown {
104 tag: u8,
106 body: &$lt [u8],
108 },
109 }
110
111 $(
112 impl<$lt> From<$($path)::+ $(<$plt>)?> for AnyDescriptor<$lt> {
113 fn from(d: $($path)::+ $(<$plt>)?) -> Self {
114 Self::$variant(d)
115 }
116 }
117 )+
118 $($(
119 impl<$lt> From<$($nd_path)::+ $(<$nd_plt>)?> for AnyDescriptor<$lt> {
120 fn from(d: $($nd_path)::+ $(<$nd_plt>)?) -> Self {
121 Self::$nd_variant(d)
122 }
123 }
124 )+)?
125
126 impl<$lt> AnyDescriptor<$lt> {
127 pub const DISPATCHED_TAGS: &'static [u8] = &[$($tag),+];
130
131 #[must_use]
137 pub fn name(&self) -> &'static str {
138 match self {
139 $(
140 Self::$variant(_) =>
141 <$($path)::+ as crate::traits::DescriptorDef>::NAME,
142 )+
143 $($(
144 Self::$nd_variant(_) =>
145 <$($nd_path)::+ as crate::traits::DescriptorDef>::NAME,
146 )+)?
147 Self::Other { .. } => "CUSTOM",
148 Self::Unknown { .. } => "UNKNOWN",
149 }
150 }
151
152 pub(crate) fn dispatch(tag: u8, full: &$lt [u8]) -> Option<crate::Result<Self>> {
158 use dvb_common::Parse;
159 match tag {
160 $(
161 $tag => Some(<$($path)::+>::parse(full).map(Self::$variant)),
162 )+
163 _ => None,
164 }
165 }
166 }
167
168 #[cfg(test)]
169 mod macro_drift {
170 #[test]
171 fn tag_literals_match_descriptor_def() {
172 use crate::traits::DescriptorDef;
173 $(
174 assert_eq!(
175 $tag,
176 <$($path)::+ as DescriptorDef>::TAG,
177 concat!("tag literal drift for ", stringify!($variant)),
178 );
179 assert!(
180 !<$($path)::+ as DescriptorDef>::NAME.is_empty(),
181 concat!("empty NAME for ", stringify!($variant)),
182 );
183 )+
184 $($(
185 assert!(
186 !<$($nd_path)::+ as DescriptorDef>::NAME.is_empty(),
187 concat!("empty NAME for ", stringify!($nd_variant)),
188 );
189 )+)?
190 }
191 }
192 };
193}
194
195declare_descriptors! {'a;
196 Registration = 0x05 => crate::descriptors::registration::RegistrationDescriptor<'a>,
198 DataStreamAlignment = 0x06 => crate::descriptors::data_stream_alignment::DataStreamAlignmentDescriptor,
199 Ca = 0x09 => crate::descriptors::ca::CaDescriptor<'a>,
200 Iso639Language = 0x0A => crate::descriptors::iso_639_language::Iso639LanguageDescriptor,
201 PrivateDataIndicator = 0x0F => crate::descriptors::private_data_indicator::PrivateDataIndicatorDescriptor,
202 NetworkName = 0x40 => crate::descriptors::network_name::NetworkNameDescriptor<'a>,
204 ServiceList = 0x41 => crate::descriptors::service_list::ServiceListDescriptor,
205 Stuffing = 0x42 => crate::descriptors::stuffing::StuffingDescriptor<'a>,
206 SatelliteDeliverySystem = 0x43 => crate::descriptors::satellite_delivery_system::SatelliteDeliverySystemDescriptor,
207 CableDeliverySystem = 0x44 => crate::descriptors::cable_delivery_system::CableDeliverySystemDescriptor,
208 VbiData = 0x45 => crate::descriptors::vbi_data::VbiDataDescriptor<'a>,
209 VbiTeletext = 0x46 => crate::descriptors::vbi_teletext::VbiTeletextDescriptor,
210 BouquetName = 0x47 => crate::descriptors::bouquet_name::BouquetNameDescriptor<'a>,
211 Service = 0x48 => crate::descriptors::service::ServiceDescriptor<'a>,
212 CountryAvailability = 0x49 => crate::descriptors::country_availability::CountryAvailabilityDescriptor,
213 Linkage = 0x4A => crate::descriptors::linkage::LinkageDescriptor<'a>,
214 NvodReference = 0x4B => crate::descriptors::nvod_reference::NvodReferenceDescriptor,
215 TimeShiftedService = 0x4C => crate::descriptors::time_shifted_service::TimeShiftedServiceDescriptor,
216 ShortEvent = 0x4D => crate::descriptors::short_event::ShortEventDescriptor<'a>,
217 ExtendedEvent = 0x4E => crate::descriptors::extended_event::ExtendedEventDescriptor<'a>,
218 TimeShiftedEvent = 0x4F => crate::descriptors::time_shifted_event::TimeShiftedEventDescriptor,
219 Component = 0x50 => crate::descriptors::component::ComponentDescriptor<'a>,
220 Mosaic = 0x51 => crate::descriptors::mosaic::MosaicDescriptor,
221 StreamIdentifier = 0x52 => crate::descriptors::stream_identifier::StreamIdentifierDescriptor,
222 CaIdentifier = 0x53 => crate::descriptors::ca_identifier::CaIdentifierDescriptor,
223 Content = 0x54 => crate::descriptors::content::ContentDescriptor,
224 ParentalRating = 0x55 => crate::descriptors::parental_rating::ParentalRatingDescriptor,
225 Teletext = 0x56 => crate::descriptors::teletext::TeletextDescriptor,
226 Telephone = 0x57 => crate::descriptors::telephone::TelephoneDescriptor<'a>,
227 LocalTimeOffset = 0x58 => crate::descriptors::local_time_offset::LocalTimeOffsetDescriptor,
228 Subtitling = 0x59 => crate::descriptors::subtitling::SubtitlingDescriptor,
229 TerrestrialDeliverySystem = 0x5A => crate::descriptors::terrestrial_delivery_system::TerrestrialDeliverySystemDescriptor,
230 MultilingualNetworkName = 0x5B => crate::descriptors::multilingual_network_name::MultilingualNetworkNameDescriptor<'a>,
231 MultilingualBouquetName = 0x5C => crate::descriptors::multilingual_bouquet_name::MultilingualBouquetNameDescriptor<'a>,
232 MultilingualServiceName = 0x5D => crate::descriptors::multilingual_service_name::MultilingualServiceNameDescriptor<'a>,
233 MultilingualComponent = 0x5E => crate::descriptors::multilingual_component::MultilingualComponentDescriptor<'a>,
234 PrivateDataSpecifier = 0x5F => crate::descriptors::private_data_specifier::PrivateDataSpecifierDescriptor,
235 ServiceMove = 0x60 => crate::descriptors::service_move::ServiceMoveDescriptor,
236 ShortSmoothingBuffer = 0x61 => crate::descriptors::short_smoothing_buffer::ShortSmoothingBufferDescriptor<'a>,
237 FrequencyList = 0x62 => crate::descriptors::frequency_list::FrequencyListDescriptor,
238 PartialTransportStream = 0x63 => crate::descriptors::partial_transport_stream::PartialTransportStreamDescriptor,
239 DataBroadcast = 0x64 => crate::descriptors::data_broadcast::DataBroadcastDescriptor<'a>,
240 Scrambling = 0x65 => crate::descriptors::scrambling::ScramblingDescriptor,
241 DataBroadcastId = 0x66 => crate::descriptors::data_broadcast_id::DataBroadcastIdDescriptor<'a>,
242 TransportStream = 0x67 => crate::descriptors::transport_stream::TransportStreamDescriptor<'a>,
243 Dsng = 0x68 => crate::descriptors::dsng::DsngDescriptor<'a>,
244 Pdc = 0x69 => crate::descriptors::pdc::PdcDescriptor,
245 Ac3 = 0x6A => crate::descriptors::ac3::Ac3Descriptor<'a>,
246 AncillaryData = 0x6B => crate::descriptors::ancillary_data::AncillaryDataDescriptor,
247 CellList = 0x6C => crate::descriptors::cell_list::CellListDescriptor,
248 CellFrequencyLink = 0x6D => crate::descriptors::cell_frequency_link::CellFrequencyLinkDescriptor,
249 AnnouncementSupport = 0x6E => crate::descriptors::announcement_support::AnnouncementSupportDescriptor,
250 ApplicationSignalling = 0x6F => crate::descriptors::application_signalling::ApplicationSignallingDescriptor,
251 AdaptationFieldData = 0x70 => crate::descriptors::adaptation_field_data::AdaptationFieldDataDescriptor,
252 ServiceIdentifier = 0x71 => crate::descriptors::service_identifier::ServiceIdentifierDescriptor<'a>,
253 ServiceAvailability = 0x72 => crate::descriptors::service_availability::ServiceAvailabilityDescriptor,
254 DefaultAuthority = 0x73 => crate::descriptors::default_authority::DefaultAuthorityDescriptor<'a>,
255 RelatedContent = 0x74 => crate::descriptors::related_content::RelatedContentDescriptor,
256 TvaId = 0x75 => crate::descriptors::tva_id::TvaIdDescriptor,
257 ContentIdentifier = 0x76 => crate::descriptors::content_identifier::ContentIdentifierDescriptor<'a>,
258 TimeSliceFecIdentifier = 0x77 => crate::descriptors::time_slice_fec_identifier::TimeSliceFecIdentifierDescriptor<'a>,
259 EcmRepetitionRate = 0x78 => crate::descriptors::ecm_repetition_rate::EcmRepetitionRateDescriptor<'a>,
260 S2SatelliteDeliverySystem = 0x79 => crate::descriptors::s2_satellite_delivery_system::S2SatelliteDeliverySystemDescriptor,
261 EnhancedAc3 = 0x7A => crate::descriptors::enhanced_ac3::EnhancedAc3Descriptor<'a>,
262 Dts = 0x7B => crate::descriptors::dts::DtsDescriptor<'a>,
263 Aac = 0x7C => crate::descriptors::aac::AacDescriptor<'a>,
264 XaitLocation = 0x7D => crate::descriptors::xait_location::XaitLocationDescriptor,
265 FtaContentManagement = 0x7E => crate::descriptors::fta_content_management::FtaContentManagementDescriptor,
266 Extension = 0x7F => crate::descriptors::extension::ExtensionDescriptor<'a>;
267 @no_dispatch
271 LogicalChannel => crate::descriptors::logical_channel::LogicalChannelDescriptor,
272}
273
274#[must_use]
281pub fn parse_loop(bytes: &[u8]) -> DescriptorIter<'_> {
282 DescriptorIter {
283 bytes,
284 pos: 0,
285 fused: false,
286 }
287}
288
289pub(crate) fn next_loop_entry<'a>(
297 bytes: &'a [u8],
298 pos: &mut usize,
299 fused: &mut bool,
300) -> Option<crate::Result<(u8, &'a [u8])>> {
301 if *fused || *pos >= bytes.len() {
302 return None;
303 }
304 let rem = &bytes[*pos..];
305 if rem.len() < 2 {
306 *fused = true;
307 return Some(Err(crate::Error::BufferTooShort {
308 need: 2,
309 have: rem.len(),
310 what: "descriptor header in loop",
311 }));
312 }
313 let tag = rem[0];
314 let len = rem[1] as usize;
315 let total = 2 + len;
316 if rem.len() < total {
317 *fused = true;
318 return Some(Err(crate::Error::BufferTooShort {
319 need: total,
320 have: rem.len(),
321 what: "descriptor body in loop",
322 }));
323 }
324 let full = &rem[..total];
325 *pos += total;
326 Some(Ok((tag, full)))
327}
328
329#[derive(Debug, Clone)]
331pub struct DescriptorIter<'a> {
332 bytes: &'a [u8],
333 pos: usize,
334 fused: bool,
335}
336
337impl<'a> Iterator for DescriptorIter<'a> {
338 type Item = crate::Result<AnyDescriptor<'a>>;
339
340 fn next(&mut self) -> Option<Self::Item> {
341 let (tag, full) = match next_loop_entry(self.bytes, &mut self.pos, &mut self.fused)? {
342 Ok(v) => v,
343 Err(e) => return Some(Err(e)),
344 };
345 Some(match AnyDescriptor::dispatch(tag, full) {
346 Some(res) => res,
347 None => Ok(AnyDescriptor::Unknown {
348 tag,
349 body: &full[2..],
350 }),
351 })
352 }
353}
354
355impl std::iter::FusedIterator for DescriptorIter<'_> {}
356
357#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
381#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
382pub struct DescriptorLoop<'a>(&'a [u8]);
383
384const DESC_HEADER_LEN: usize = 2;
386
387impl<'a> DescriptorLoop<'a> {
388 #[must_use]
391 pub const fn new(raw: &'a [u8]) -> Self {
392 Self(raw)
393 }
394
395 #[must_use]
398 pub const fn raw(&self) -> &'a [u8] {
399 self.0
400 }
401
402 #[must_use]
406 pub fn iter(&self) -> DescriptorIter<'a> {
407 parse_loop(self.0)
408 }
409
410 pub fn raw_tags(&self) -> impl Iterator<Item = (u8, &'a [u8])> {
420 let b = self.0;
421 let mut pos = 0usize;
422 core::iter::from_fn(move || {
423 if pos + DESC_HEADER_LEN > b.len() {
424 return None;
425 }
426 let tag = b[pos];
427 let len = b[pos + 1] as usize;
428 let end = pos + DESC_HEADER_LEN + len;
429 if end > b.len() {
430 return None; }
432 let body = &b[pos + DESC_HEADER_LEN..end];
433 pos = end;
434 Some((tag, body))
435 })
436 }
437
438 #[must_use]
443 pub fn contains_tag(&self, tag: u8) -> bool {
444 let b = self.0;
445 let mut pos = 0usize;
446 while pos < b.len() {
447 if b[pos] == tag {
448 return true;
449 }
450 if pos + 1 >= b.len() {
453 break;
454 }
455 pos += DESC_HEADER_LEN + b[pos + 1] as usize;
456 }
457 false
458 }
459
460 #[must_use]
468 pub fn iter_with<'r>(
469 &self,
470 registry: &'r crate::descriptors::registry::DescriptorRegistry,
471 ) -> crate::descriptors::registry::RegistryIter<'r, 'a> {
472 registry.parse_loop(self.0)
473 }
474
475 #[must_use]
488 pub fn iter_with_extensions<'r>(
489 &self,
490 desc_reg: &'r crate::descriptors::registry::DescriptorRegistry,
491 ext_reg: &'r crate::descriptors::extension::registry::ExtensionRegistry,
492 ) -> crate::descriptors::registry::ExtRegistryIter<'r, 'a> {
493 crate::descriptors::registry::ExtRegistryIter::new(desc_reg, ext_reg, self.0)
494 }
495}
496
497impl<'a> std::ops::Deref for DescriptorLoop<'a> {
498 type Target = [u8];
502 fn deref(&self) -> &[u8] {
503 self.0
504 }
505}
506
507impl<'a> From<&'a [u8]> for DescriptorLoop<'a> {
508 fn from(raw: &'a [u8]) -> Self {
509 Self(raw)
510 }
511}
512
513impl std::fmt::Debug for DescriptorLoop<'_> {
514 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
516 write!(f, "DescriptorLoop(<{} bytes>)", self.0.len())
517 }
518}
519
520impl<'a> IntoIterator for &DescriptorLoop<'a> {
521 type Item = crate::Result<AnyDescriptor<'a>>;
522 type IntoIter = DescriptorIter<'a>;
523 fn into_iter(self) -> Self::IntoIter {
524 self.iter()
525 }
526}
527
528#[cfg(feature = "serde")]
529impl serde::Serialize for DescriptorLoop<'_> {
530 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
535 struct Entry<'a>(crate::Result<AnyDescriptor<'a>>);
536 impl serde::Serialize for Entry<'_> {
537 fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
538 match &self.0 {
539 Ok(d) => d.serialize(s),
540 Err(e) => {
541 use serde::ser::SerializeMap;
542 let mut m = s.serialize_map(Some(1))?;
543 m.serialize_entry("parseError", &e.to_string())?;
544 m.end()
545 }
546 }
547 }
548 }
549 s.collect_seq(self.iter().map(Entry))
550 }
551}
552#[cfg(test)]
558mod tests {
559 use super::*;
560
561 #[test]
562 fn contains_tag_detects_present_tag_regardless_of_body() {
563 assert!(DescriptorLoop::new(&[0x6A, 0x01, 0x80]).contains_tag(0x6A));
565 assert!(DescriptorLoop::new(&[0x6A, 0x00]).contains_tag(0x6A));
567 assert!(DescriptorLoop::new(&[0x6A, 0x01]).contains_tag(0x6A));
569 assert!(DescriptorLoop::new(&[0x09, 0x02, 0x00, 0x00, 0x7A, 0x00]).contains_tag(0x7A));
571 assert!(!DescriptorLoop::new(&[0x09, 0x02, 0x00, 0x00]).contains_tag(0x6A));
573 assert!(!DescriptorLoop::new(&[]).contains_tag(0x6A));
574 }
575
576 #[test]
577 fn raw_tags_walks_tlv_without_typed_parsing() {
578 let loop_ = DescriptorLoop::new(&[0x6A, 0x00, 0x40, 0x02, 0xAA, 0xBB]);
580 let pairs: Vec<_> = loop_.raw_tags().collect();
581 assert_eq!(pairs, vec![(0x6A, &[][..]), (0x40, &[0xAA, 0xBB][..])]);
582 let trunc = DescriptorLoop::new(&[0x40, 0x01, 0xAA, 0x6A, 0x05]);
585 let tags: Vec<u8> = trunc.raw_tags().map(|(t, _)| t).collect();
586 assert_eq!(tags, vec![0x40]); assert!(trunc.contains_tag(0x6A));
589 }
590
591 #[test]
592 fn unknown_tag_yields_unknown_with_body_sans_header() {
593 let bytes = [0xA7, 0x02, 0xDE, 0xAD];
595 let items: Vec<_> = parse_loop(&bytes).collect();
596 assert_eq!(items.len(), 1);
597 match items[0].as_ref().unwrap() {
598 AnyDescriptor::Unknown { tag, body } => {
599 assert_eq!(*tag, 0xA7);
600 assert_eq!(*body, &[0xDE, 0xAD]);
601 }
602 other => panic!("expected Unknown, got {other:?}"),
603 }
604 }
605
606 #[test]
607 fn empty_loop_yields_nothing() {
608 assert_eq!(parse_loop(&[]).count(), 0);
609 }
610
611 #[test]
612 fn logical_channel_0x83_is_not_dispatched() {
613 let bytes = [0x83, 0x04, 0x00, 0x01, 0xFC, 0x01];
615 let items: Vec<_> = parse_loop(&bytes).collect();
616 assert_eq!(items.len(), 1);
617 assert!(matches!(
618 items[0].as_ref().unwrap(),
619 AnyDescriptor::Unknown { tag: 0x83, .. }
620 ));
621 }
622
623 #[test]
624 fn descriptor_loop_iter_matches_parse_loop() {
625 let raw = [
626 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, 0xA7, 0x02, 0xCA, 0xFE, ];
629 let via_loop: Vec<_> = DescriptorLoop::new(&raw)
630 .iter()
631 .map(|r| format!("{r:?}"))
632 .collect();
633 let via_fn: Vec<_> = parse_loop(&raw).map(|r| format!("{r:?}")).collect();
634 assert_eq!(via_loop, via_fn);
635 assert_eq!(DescriptorLoop::new(&raw).raw(), &raw[..]);
637 assert_eq!(DescriptorLoop::new(&raw).len(), raw.len());
638 let count = (&DescriptorLoop::new(&raw)).into_iter().count();
640 assert_eq!(count, 2);
641 }
642
643 #[test]
644 fn descriptor_loop_debug_is_cheap() {
645 let raw = [0x4D, 0x02, 0x01, 0x02];
646 assert_eq!(
647 format!("{:?}", DescriptorLoop::new(&raw)),
648 "DescriptorLoop(<4 bytes>)"
649 );
650 }
651
652 #[test]
653 fn iter_with_custom_tag_yields_other() {
654 use crate::descriptors::registry::DescriptorRegistry;
655 use crate::traits::DescriptorDef;
656
657 #[derive(Debug, PartialEq, Eq)]
658 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
659 struct MyTag0xA7 {
660 x: u8,
661 }
662
663 impl<'a> dvb_common::Parse<'a> for MyTag0xA7 {
664 type Error = crate::error::Error;
665 fn parse(bytes: &'a [u8]) -> crate::Result<Self> {
666 if bytes.len() < 3 {
667 return Err(crate::error::Error::BufferTooShort {
668 need: 3,
669 have: bytes.len(),
670 what: "MyTag0xA7",
671 });
672 }
673 Ok(Self { x: bytes[2] })
674 }
675 }
676
677 impl<'a> DescriptorDef<'a> for MyTag0xA7 {
678 const TAG: u8 = 0xA7;
679 const NAME: &'static str = "MY_TAG_0xA7";
680 }
681
682 let mut reg = DescriptorRegistry::new();
683 reg.register::<MyTag0xA7>();
684
685 let raw = [
686 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, 0xA7, 0x02, 0xCA, 0xFE,
687 ];
688 let loop_ = DescriptorLoop::new(&raw);
689 let items: Vec<_> = loop_.iter_with(®).collect::<Result<_, _>>().unwrap();
690 assert_eq!(items.len(), 2);
691 assert!(matches!(items[0], AnyDescriptor::ShortEvent(_)));
692 match &items[1] {
693 AnyDescriptor::Other { tag, value } => {
694 assert_eq!(*tag, 0xA7);
695 assert_eq!(value.downcast_ref::<MyTag0xA7>().unwrap().x, 0xCA);
696 }
697 other => panic!("expected Other, got {other:?}"),
698 }
699 }
700
701 #[test]
702 fn iter_with_empty_registry_matches_iter_for_builtin() {
703 let raw = [
704 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, 0xA7, 0x02, 0xCA, 0xFE,
705 ];
706 let loop_ = DescriptorLoop::new(&raw);
707 let reg = crate::descriptors::registry::DescriptorRegistry::new();
708 let via_iter: Vec<_> = loop_.iter().collect();
709 let via_iter_with: Vec<_> = loop_.iter_with(®).collect();
710
711 assert_eq!(via_iter.len(), via_iter_with.len());
712 for (a, b) in via_iter.iter().zip(via_iter_with.iter()) {
713 match (a, b) {
714 (Ok(AnyDescriptor::ShortEvent(_)), Ok(AnyDescriptor::ShortEvent(_))) => {}
715 (
716 Ok(AnyDescriptor::Unknown { tag: t1, body: b1 }),
717 Ok(AnyDescriptor::Unknown { tag: t2, body: b2 }),
718 ) => {
719 assert_eq!(t1, t2);
720 assert_eq!(b1, b2);
721 }
722 (Err(_), Err(_)) => {}
723 (l, r) => panic!("mismatch: {l:?} vs {r:?}"),
724 }
725 }
726 }
727
728 #[cfg(feature = "serde")]
729 #[test]
730 fn descriptor_loop_serializes_typed_unknown_and_parse_error() {
731 let raw = [
735 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, 0xA7, 0x02, 0xCA, 0xFE, 0x55, 0x05, 0x00, ];
739 let v = serde_json::to_value(DescriptorLoop::new(&raw)).unwrap();
740 let arr = v.as_array().expect("sequence");
741 assert_eq!(arr.len(), 3);
742 assert!(arr[0].get("shortEvent").is_some(), "got {}", arr[0]);
744 assert_eq!(arr[0]["shortEvent"]["event_name"], "Hi");
745 let unknown = arr[1].get("unknown").expect("unknown variant");
747 assert_eq!(unknown["tag"], 0xA7);
748 assert_eq!(unknown["body"], serde_json::json!([0xCA, 0xFE]));
749 assert!(
751 arr[2].get("parseError").is_some(),
752 "expected parseError, got {}",
753 arr[2]
754 );
755 }
756}