1use alloc::boxed::Box;
69use alloc::collections::BTreeMap;
70use core::any::Any;
71
72use crate::descriptors::any::AnyDescriptor;
73use crate::descriptors::private_data_specifier::PDS_NORDIG;
74
75#[cfg(not(feature = "serde"))]
87pub trait DescriptorObject: core::fmt::Debug + Any + Send + Sync {
88 fn as_any(&self) -> &dyn Any;
90}
91
92#[cfg(feature = "serde")]
100pub trait DescriptorObject: core::fmt::Debug + Any + Send + Sync + erased_serde::Serialize {
101 fn as_any(&self) -> &dyn Any;
103}
104
105#[cfg(not(feature = "serde"))]
107impl<T> DescriptorObject for T
108where
109 T: core::fmt::Debug + Any + Send + Sync,
110{
111 fn as_any(&self) -> &dyn Any {
112 self
113 }
114}
115
116#[cfg(feature = "serde")]
118impl<T> DescriptorObject for T
119where
120 T: core::fmt::Debug + Any + Send + Sync + serde::Serialize,
121{
122 fn as_any(&self) -> &dyn Any {
123 self
124 }
125}
126
127impl dyn DescriptorObject {
139 #[must_use]
144 pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
145 self.as_any().downcast_ref::<T>()
146 }
147
148 #[must_use]
150 pub fn is<T: Any>(&self) -> bool {
151 self.as_any().is::<T>()
152 }
153}
154
155#[cfg(feature = "serde")]
167#[allow(clippy::borrowed_box)]
168pub(crate) fn serialize_erased<S: serde::Serializer>(
169 v: &Box<dyn DescriptorObject>,
170 s: S,
171) -> Result<S::Ok, S::Error> {
172 erased_serde::serialize(&**v, s)
173}
174
175pub(crate) type CustomParse =
182 Box<dyn for<'a> Fn(&'a [u8]) -> crate::Result<Box<dyn DescriptorObject>> + Send + Sync>;
183
184#[derive(Default)]
214pub struct DescriptorRegistry {
215 custom: BTreeMap<(Option<u32>, u8), CustomParse>,
216 logical_channel: bool,
217 logical_channel_pds: alloc::collections::BTreeSet<u32>,
218}
219
220impl DescriptorRegistry {
221 #[must_use]
223 pub fn new() -> Self {
224 Self::default()
225 }
226
227 pub fn register<T>(&mut self) -> &mut Self
248 where
249 T: for<'a> crate::traits::DescriptorDef<'a> + DescriptorObject + 'static,
250 {
251 let tag = <T as crate::traits::DescriptorDef<'static>>::TAG;
252 self.custom.insert(
253 (None, tag),
254 Box::new(|b| {
255 Ok(Box::new(<T as broadcast_common::Parse>::parse(b)?)
256 as Box<dyn DescriptorObject>)
257 }),
258 );
259 self
260 }
261
262 pub fn register_for_pds<T>(&mut self, pds: u32) -> &mut Self
274 where
275 T: for<'a> crate::traits::DescriptorDef<'a> + DescriptorObject + 'static,
276 {
277 let tag = <T as crate::traits::DescriptorDef<'static>>::TAG;
278 self.custom.insert(
279 (Some(pds), tag),
280 Box::new(|b| {
281 Ok(Box::new(<T as broadcast_common::Parse>::parse(b)?)
282 as Box<dyn DescriptorObject>)
283 }),
284 );
285 self
286 }
287
288 pub fn with_logical_channel(&mut self) -> &mut Self {
294 self.logical_channel = true;
295 self
296 }
297
298 pub fn with_logical_channel_for_pds(&mut self, pds: u32) -> &mut Self {
310 self.logical_channel_pds.insert(pds);
311 self
312 }
313
314 pub fn with_nordig_lcn(&mut self) -> &mut Self {
318 self.register_for_pds::<crate::descriptors::nordig::NordigLogicalChannelV1>(PDS_NORDIG);
319 self.register_for_pds::<crate::descriptors::nordig::NordigLogicalChannelV2>(PDS_NORDIG);
320 self
321 }
322
323 #[must_use]
333 pub fn parse_loop<'r, 'a>(&'r self, bytes: &'a [u8]) -> RegistryIter<'r, 'a> {
334 RegistryIter {
335 registry: self,
336 bytes,
337 pos: 0,
338 fused: false,
339 current_pds: None,
340 }
341 }
342}
343
344pub struct RegistryIter<'r, 'a> {
352 registry: &'r DescriptorRegistry,
353 bytes: &'a [u8],
354 pos: usize,
355 fused: bool,
356 current_pds: Option<u32>,
357}
358
359pub(crate) fn dispatch_entry<'a>(
365 registry: &DescriptorRegistry,
366 current_pds: Option<u32>,
367 tag: u8,
368 full: &'a [u8],
369) -> crate::Result<AnyDescriptor<'a>> {
370 if let Some(pds) = current_pds {
371 if let Some(parse_fn) = registry.custom.get(&(Some(pds), tag)) {
372 return parse_fn(full).map(|value| AnyDescriptor::Other { tag, value });
373 }
374 }
375 if let Some(parse_fn) = registry.custom.get(&(None, tag)) {
376 return parse_fn(full).map(|value| AnyDescriptor::Other { tag, value });
377 }
378 if let Some(pds) = current_pds {
379 if tag == crate::descriptors::logical_channel::TAG
380 && registry.logical_channel_pds.contains(&pds)
381 {
382 use broadcast_common::Parse;
383 return crate::descriptors::logical_channel::LogicalChannelDescriptor::parse(full)
384 .map(AnyDescriptor::LogicalChannel);
385 }
386 }
387 if registry.logical_channel && tag == crate::descriptors::logical_channel::TAG {
388 use broadcast_common::Parse;
389 return crate::descriptors::logical_channel::LogicalChannelDescriptor::parse(full)
390 .map(AnyDescriptor::LogicalChannel);
391 }
392 if let Some(res) = AnyDescriptor::dispatch(tag, full) {
393 return res;
394 }
395 Ok(AnyDescriptor::Unknown {
396 tag,
397 body: &full[2..],
398 })
399}
400
401fn update_pds(current: &mut Option<u32>, tag: u8, full: &[u8]) {
402 if tag == crate::descriptors::private_data_specifier::TAG {
403 use broadcast_common::Parse;
404 if let Ok(pds) =
405 crate::descriptors::private_data_specifier::PrivateDataSpecifierDescriptor::parse(full)
406 {
407 *current = Some(pds.private_data_specifier);
408 }
409 }
410}
411
412impl<'a> Iterator for RegistryIter<'_, 'a> {
413 type Item = crate::Result<AnyDescriptor<'a>>;
414
415 fn next(&mut self) -> Option<Self::Item> {
416 let (tag, full) = match crate::descriptors::any::next_loop_entry(
417 self.bytes,
418 &mut self.pos,
419 &mut self.fused,
420 )? {
421 Ok(v) => v,
422 Err(e) => return Some(Err(e)),
423 };
424
425 update_pds(&mut self.current_pds, tag, full);
426
427 Some(dispatch_entry(self.registry, self.current_pds, tag, full))
428 }
429}
430
431impl core::iter::FusedIterator for RegistryIter<'_, '_> {}
432
433#[derive(Debug)]
445#[cfg_attr(feature = "serde", derive(serde::Serialize))]
446#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
447#[non_exhaustive]
448pub enum ExtIterItem<'a> {
449 Descriptor(AnyDescriptor<'a>),
451 CustomExtension {
453 tag_extension: u8,
455 #[cfg_attr(
458 feature = "serde",
459 serde(serialize_with = "super::extension::registry::serialize_erased")
460 )]
461 value: Box<dyn super::extension::registry::ExtensionObject>,
462 },
463}
464
465pub struct ExtRegistryIter<'r, 'a> {
475 desc_reg: &'r DescriptorRegistry,
476 ext_reg: &'r super::extension::registry::ExtensionRegistry,
477 bytes: &'a [u8],
478 pos: usize,
479 fused: bool,
480 current_pds: Option<u32>,
481}
482
483impl<'r, 'a> ExtRegistryIter<'r, 'a> {
484 pub(crate) fn new(
485 desc_reg: &'r DescriptorRegistry,
486 ext_reg: &'r super::extension::registry::ExtensionRegistry,
487 bytes: &'a [u8],
488 ) -> Self {
489 Self {
490 desc_reg,
491 ext_reg,
492 bytes,
493 pos: 0,
494 fused: false,
495 current_pds: None,
496 }
497 }
498}
499
500impl<'a> Iterator for ExtRegistryIter<'_, 'a> {
501 type Item = crate::Result<ExtIterItem<'a>>;
502
503 fn next(&mut self) -> Option<Self::Item> {
504 let (tag, full) = match crate::descriptors::any::next_loop_entry(
505 self.bytes,
506 &mut self.pos,
507 &mut self.fused,
508 )? {
509 Ok(v) => v,
510 Err(e) => return Some(Err(e)),
511 };
512
513 update_pds(&mut self.current_pds, tag, full);
514
515 let len = full.len() - 2;
516 if tag == crate::descriptors::extension::TAG && len >= 1 {
517 let tag_extension = full[2];
518 if self.ext_reg.has_custom(tag_extension) {
519 return Some(match self.ext_reg.parse_body(tag_extension, &full[3..]) {
520 Ok(super::extension::registry::RegisteredExtension::Custom {
521 tag_extension,
522 value,
523 }) => Ok(ExtIterItem::CustomExtension {
524 tag_extension,
525 value,
526 }),
527 Ok(super::extension::registry::RegisteredExtension::Builtin(d)) => {
528 Ok(ExtIterItem::Descriptor(AnyDescriptor::Extension(d)))
529 }
530 Err(e) => Err(e),
531 });
532 }
533 }
534
535 Some(
536 dispatch_entry(self.desc_reg, self.current_pds, tag, full).map(ExtIterItem::Descriptor),
537 )
538 }
539}
540
541impl core::iter::FusedIterator for ExtRegistryIter<'_, '_> {}
542
543#[cfg(test)]
544mod tests {
545 use super::*;
546 use crate::descriptors::private_data_specifier;
547 use crate::descriptors::private_data_specifier::{PDS_EACEM, PDS_NORDIG};
548 use crate::traits::DescriptorDef;
549
550 #[test]
551 fn pds_constants_match_the_register() {
552 assert_eq!(
555 private_data_specifier::private_data_specifier_name(PDS_EACEM),
556 Some("EACEM/EICTA")
557 );
558 assert_eq!(
559 private_data_specifier::private_data_specifier_name(PDS_NORDIG),
560 Some("NorDig")
561 );
562 }
563
564 #[derive(Debug, PartialEq, Eq)]
565 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
566 struct PdsEacem {
567 v: u8,
568 }
569
570 impl<'a> broadcast_common::Parse<'a> for PdsEacem {
571 type Error = crate::error::Error;
572 fn parse(bytes: &'a [u8]) -> crate::Result<Self> {
573 if bytes.len() < 3 {
574 return Err(crate::error::Error::BufferTooShort {
575 need: 3,
576 have: bytes.len(),
577 what: "PdsEacem",
578 });
579 }
580 Ok(Self { v: bytes[2] })
581 }
582 }
583
584 impl DescriptorDef<'_> for PdsEacem {
585 const TAG: u8 = 0x83;
586 const NAME: &'static str = "PDS_EACEM";
587 }
588
589 #[derive(Debug, PartialEq, Eq)]
590 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
591 struct PdsNordig {
592 w: u8,
593 }
594
595 impl<'a> broadcast_common::Parse<'a> for PdsNordig {
596 type Error = crate::error::Error;
597 fn parse(bytes: &'a [u8]) -> crate::Result<Self> {
598 if bytes.len() < 3 {
599 return Err(crate::error::Error::BufferTooShort {
600 need: 3,
601 have: bytes.len(),
602 what: "PdsNordig",
603 });
604 }
605 Ok(Self { w: bytes[2] })
606 }
607 }
608
609 impl DescriptorDef<'_> for PdsNordig {
610 const TAG: u8 = 0x83;
611 const NAME: &'static str = "PDS_NORDIG";
612 }
613
614 #[derive(Debug, PartialEq, Eq)]
615 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
616 struct PdsAgnostic {
617 z: u8,
618 }
619
620 impl<'a> broadcast_common::Parse<'a> for PdsAgnostic {
621 type Error = crate::error::Error;
622 fn parse(bytes: &'a [u8]) -> crate::Result<Self> {
623 if bytes.len() < 3 {
624 return Err(crate::error::Error::BufferTooShort {
625 need: 3,
626 have: bytes.len(),
627 what: "PdsAgnostic",
628 });
629 }
630 Ok(Self { z: bytes[2] })
631 }
632 }
633
634 impl DescriptorDef<'_> for PdsAgnostic {
635 const TAG: u8 = 0x84;
636 const NAME: &'static str = "PDS_AGNOSTIC";
637 }
638
639 fn pds_descriptor(pds: u32) -> Vec<u8> {
640 let mut v = vec![private_data_specifier::TAG, 4];
641 v.extend_from_slice(&pds.to_be_bytes());
642 v
643 }
644
645 #[test]
646 fn pds_scoped_same_tag_resolves_by_pds() {
647 let mut reg = DescriptorRegistry::new();
648 reg.register_for_pds::<PdsEacem>(PDS_EACEM);
649 reg.register_for_pds::<PdsNordig>(PDS_NORDIG);
650
651 let mut bytes = Vec::new();
652 bytes.extend_from_slice(&pds_descriptor(PDS_EACEM));
653 bytes.extend_from_slice(&[0x83, 0x01, 0xAA]);
654
655 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
656 assert_eq!(items.len(), 2);
657 assert!(matches!(items[0], AnyDescriptor::PrivateDataSpecifier(_)));
658 match &items[1] {
659 AnyDescriptor::Other { tag, value } => {
660 assert_eq!(*tag, 0x83);
661 let c = value.downcast_ref::<PdsEacem>().unwrap();
662 assert_eq!(c.v, 0xAA);
663 }
664 other => panic!("expected Other (PdsEacem), got {other:?}"),
665 }
666
667 let mut bytes2 = Vec::new();
668 bytes2.extend_from_slice(&pds_descriptor(PDS_NORDIG));
669 bytes2.extend_from_slice(&[0x83, 0x01, 0xBB]);
670
671 let items2: Vec<_> = reg.parse_loop(&bytes2).collect::<Result<_, _>>().unwrap();
672 match &items2[1] {
673 AnyDescriptor::Other { tag, value } => {
674 assert_eq!(*tag, 0x83);
675 let c = value.downcast_ref::<PdsNordig>().unwrap();
676 assert_eq!(c.w, 0xBB);
677 }
678 other => panic!("expected Other (PdsNordig), got {other:?}"),
679 }
680 }
681
682 #[test]
683 fn pds_scoped_does_not_match_wrong_pds() {
684 let mut reg = DescriptorRegistry::new();
685 reg.register_for_pds::<PdsEacem>(PDS_EACEM);
686
687 let mut bytes = Vec::new();
688 bytes.extend_from_slice(&pds_descriptor(PDS_NORDIG));
689 bytes.extend_from_slice(&[0x83, 0x01, 0xCC]);
690
691 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
692 assert_eq!(items.len(), 2);
693 assert!(matches!(items[0], AnyDescriptor::PrivateDataSpecifier(_)));
694 match &items[1] {
695 AnyDescriptor::Unknown { tag, .. } => assert_eq!(*tag, 0x83),
696 other => panic!("expected Unknown (wrong PDS), got {other:?}"),
697 }
698 }
699
700 #[test]
701 fn pds_agnostic_matches_without_pds() {
702 let mut reg = DescriptorRegistry::new();
703 reg.register::<PdsAgnostic>();
704
705 let bytes = [0x84, 0x01, 0xDD];
706 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
707 assert_eq!(items.len(), 1);
708 match &items[0] {
709 AnyDescriptor::Other { tag, value } => {
710 assert_eq!(*tag, 0x84);
711 let c = value.downcast_ref::<PdsAgnostic>().unwrap();
712 assert_eq!(c.z, 0xDD);
713 }
714 other => panic!("expected Other, got {other:?}"),
715 }
716 }
717
718 #[test]
719 fn pds_scoped_takes_precedence_over_agnostic() {
720 #[derive(Debug, PartialEq, Eq)]
724 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
725 struct Agnostic83 {
726 a: u8,
727 }
728
729 impl<'a> broadcast_common::Parse<'a> for Agnostic83 {
730 type Error = crate::error::Error;
731 fn parse(bytes: &'a [u8]) -> crate::Result<Self> {
732 if bytes.len() < 3 {
733 return Err(crate::error::Error::BufferTooShort {
734 need: 3,
735 have: bytes.len(),
736 what: "Agnostic83",
737 });
738 }
739 Ok(Self { a: bytes[2] })
740 }
741 }
742
743 impl DescriptorDef<'_> for Agnostic83 {
744 const TAG: u8 = 0x83;
745 const NAME: &'static str = "AGNOSTIC_83";
746 }
747
748 let mut reg = DescriptorRegistry::new();
749 reg.register::<Agnostic83>();
750 reg.register_for_pds::<PdsEacem>(PDS_EACEM);
751
752 let items: Vec<_> = reg
754 .parse_loop(&[0x83, 0x01, 0xEE])
755 .collect::<Result<_, _>>()
756 .unwrap();
757 match &items[0] {
758 AnyDescriptor::Other { value, .. } => {
759 assert!(value.downcast_ref::<Agnostic83>().is_some());
760 assert!(value.downcast_ref::<PdsEacem>().is_none());
761 }
762 other => panic!("expected Other, got {other:?}"),
763 }
764
765 let mut bytes = Vec::new();
767 bytes.extend_from_slice(&pds_descriptor(PDS_EACEM));
768 bytes.extend_from_slice(&[0x83, 0x01, 0xFF]);
769 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
770 match &items[1] {
771 AnyDescriptor::Other { value, .. } => {
772 assert!(value.downcast_ref::<PdsEacem>().is_some());
773 assert!(value.downcast_ref::<Agnostic83>().is_none());
774 }
775 other => panic!("expected Other, got {other:?}"),
776 }
777 }
778
779 fn logical_channel_descriptor(service_id: u16, visible: bool, lcn: u16) -> Vec<u8> {
780 let mut v = vec![crate::descriptors::logical_channel::TAG, 4];
782 v.extend_from_slice(&service_id.to_be_bytes());
783 let flags = (u8::from(visible) << 7) | ((lcn >> 8) as u8 & 0x03);
784 v.push(flags);
785 v.push((lcn & 0xFF) as u8);
786 v
787 }
788
789 #[test]
790 fn logical_channel_pds_scoped_matches_correct_pds() {
791 let mut reg = DescriptorRegistry::new();
792 reg.with_logical_channel_for_pds(PDS_EACEM);
793
794 let mut bytes = Vec::new();
795 bytes.extend_from_slice(&pds_descriptor(PDS_EACEM));
796 bytes.extend_from_slice(&logical_channel_descriptor(0x1234, true, 101));
797
798 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
799 assert_eq!(items.len(), 2);
800 assert!(matches!(items[0], AnyDescriptor::PrivateDataSpecifier(_)));
801 match &items[1] {
802 AnyDescriptor::LogicalChannel(lc) => {
803 assert_eq!(lc.entries.len(), 1);
804 assert_eq!(lc.entries[0].service_id, 0x1234);
805 assert!(lc.entries[0].visible_service);
806 assert_eq!(lc.entries[0].logical_channel_number, 101);
807 }
808 other => panic!("expected LogicalChannel, got {other:?}"),
809 }
810 }
811
812 #[test]
813 fn logical_channel_pds_scoped_rejects_no_pds() {
814 let mut reg = DescriptorRegistry::new();
815 reg.with_logical_channel_for_pds(PDS_EACEM);
816
817 let bytes = logical_channel_descriptor(0x1234, true, 101);
818 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
819 assert_eq!(items.len(), 1);
820 assert!(matches!(
821 &items[0],
822 AnyDescriptor::Unknown { tag: 0x83, .. }
823 ));
824 }
825
826 #[test]
827 fn logical_channel_pds_scoped_rejects_wrong_pds() {
828 let mut reg = DescriptorRegistry::new();
829 reg.with_logical_channel_for_pds(PDS_EACEM);
830
831 let mut bytes = Vec::new();
832 bytes.extend_from_slice(&pds_descriptor(PDS_NORDIG));
833 bytes.extend_from_slice(&logical_channel_descriptor(0x1234, true, 101));
834
835 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
836 assert_eq!(items.len(), 2);
837 assert!(matches!(items[0], AnyDescriptor::PrivateDataSpecifier(_)));
838 assert!(matches!(
839 &items[1],
840 AnyDescriptor::Unknown { tag: 0x83, .. }
841 ));
842 }
843
844 #[test]
845 fn logical_channel_pds_scoped_multiple_pds() {
846 let mut reg = DescriptorRegistry::new();
847 reg.with_logical_channel_for_pds(PDS_EACEM);
848 reg.with_logical_channel_for_pds(PDS_NORDIG);
849
850 let mut bytes = Vec::new();
852 bytes.extend_from_slice(&pds_descriptor(PDS_EACEM));
853 bytes.extend_from_slice(&logical_channel_descriptor(0x0001, true, 1));
854 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
855 assert!(matches!(&items[1], AnyDescriptor::LogicalChannel(_)));
856
857 let mut bytes2 = Vec::new();
859 bytes2.extend_from_slice(&pds_descriptor(PDS_NORDIG));
860 bytes2.extend_from_slice(&logical_channel_descriptor(0x0002, false, 2));
861 let items2: Vec<_> = reg.parse_loop(&bytes2).collect::<Result<_, _>>().unwrap();
862 assert!(matches!(&items2[1], AnyDescriptor::LogicalChannel(_)));
863 }
864
865 #[test]
866 fn iter_with_extensions_surfaces_custom_extension() {
867 use crate::descriptors::any::{AnyDescriptor, DescriptorLoop};
868 use crate::descriptors::extension::registry::ExtensionRegistry;
869
870 #[derive(Debug, PartialEq, Eq)]
871 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
872 struct MyCustomExt {
873 payload: Vec<u8>,
874 }
875
876 impl<'a> broadcast_common::Parse<'a> for MyCustomExt {
877 type Error = crate::error::Error;
878 fn parse(sel: &'a [u8]) -> crate::Result<Self> {
879 Ok(Self {
880 payload: sel.to_vec(),
881 })
882 }
883 }
884
885 impl crate::descriptors::extension::ExtensionBodyDef<'_> for MyCustomExt {
886 const TAG_EXTENSION: u8 = 0x42;
887 const NAME: &'static str = "MY_CUSTOM_EXT";
888 }
889
890 let mut ext_reg = ExtensionRegistry::new();
891 ext_reg.register::<MyCustomExt>();
892
893 let desc_reg = DescriptorRegistry::new();
894
895 let mut loop_bytes = vec![
897 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, ];
899 loop_bytes.extend_from_slice(&[0x7F, 0x03, 0x42, 0xAB, 0xCD]);
901
902 let dl = DescriptorLoop::new(&loop_bytes);
903 let items: Vec<_> = dl
904 .iter_with_extensions(&desc_reg, &ext_reg)
905 .collect::<Result<_, _>>()
906 .unwrap();
907 assert_eq!(items.len(), 2);
908 assert!(matches!(
910 &items[0],
911 ExtIterItem::Descriptor(AnyDescriptor::ShortEvent(_))
912 ));
913 match &items[1] {
915 ExtIterItem::CustomExtension {
916 tag_extension,
917 value,
918 } => {
919 assert_eq!(*tag_extension, 0x42);
920 let concrete = value.downcast_ref::<MyCustomExt>().unwrap();
921 assert_eq!(concrete.payload, &[0xAB, 0xCD]);
922 }
923 other => panic!("expected CustomExtension, got {other:?}"),
924 }
925 }
926}