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 && let Some(parse_fn) = registry.custom.get(&(Some(pds), tag))
372 {
373 return parse_fn(full).map(|value| AnyDescriptor::Other { tag, value });
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 && 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 if registry.logical_channel && tag == crate::descriptors::logical_channel::TAG {
387 use broadcast_common::Parse;
388 return crate::descriptors::logical_channel::LogicalChannelDescriptor::parse(full)
389 .map(AnyDescriptor::LogicalChannel);
390 }
391 if let Some(res) = AnyDescriptor::dispatch(tag, full) {
392 return res;
393 }
394 Ok(AnyDescriptor::Unknown {
395 tag,
396 body: &full[2..],
397 })
398}
399
400fn update_pds(current: &mut Option<u32>, tag: u8, full: &[u8]) {
401 if tag == crate::descriptors::private_data_specifier::TAG {
402 use broadcast_common::Parse;
403 if let Ok(pds) =
404 crate::descriptors::private_data_specifier::PrivateDataSpecifierDescriptor::parse(full)
405 {
406 *current = Some(pds.private_data_specifier);
407 }
408 }
409}
410
411impl<'a> Iterator for RegistryIter<'_, 'a> {
412 type Item = crate::Result<AnyDescriptor<'a>>;
413
414 fn next(&mut self) -> Option<Self::Item> {
415 let (tag, full) = match crate::descriptors::any::next_loop_entry(
416 self.bytes,
417 &mut self.pos,
418 &mut self.fused,
419 )? {
420 Ok(v) => v,
421 Err(e) => return Some(Err(e)),
422 };
423
424 update_pds(&mut self.current_pds, tag, full);
425
426 Some(dispatch_entry(self.registry, self.current_pds, tag, full))
427 }
428}
429
430impl core::iter::FusedIterator for RegistryIter<'_, '_> {}
431
432#[derive(Debug)]
444#[cfg_attr(feature = "serde", derive(serde::Serialize))]
445#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
446#[non_exhaustive]
447pub enum ExtIterItem<'a> {
448 Descriptor(AnyDescriptor<'a>),
450 CustomExtension {
452 tag_extension: u8,
454 #[cfg_attr(
457 feature = "serde",
458 serde(serialize_with = "super::extension::registry::serialize_erased")
459 )]
460 value: Box<dyn super::extension::registry::ExtensionObject>,
461 },
462}
463
464pub struct ExtRegistryIter<'r, 'a> {
474 desc_reg: &'r DescriptorRegistry,
475 ext_reg: &'r super::extension::registry::ExtensionRegistry,
476 bytes: &'a [u8],
477 pos: usize,
478 fused: bool,
479 current_pds: Option<u32>,
480}
481
482impl<'r, 'a> ExtRegistryIter<'r, 'a> {
483 pub(crate) fn new(
484 desc_reg: &'r DescriptorRegistry,
485 ext_reg: &'r super::extension::registry::ExtensionRegistry,
486 bytes: &'a [u8],
487 ) -> Self {
488 Self {
489 desc_reg,
490 ext_reg,
491 bytes,
492 pos: 0,
493 fused: false,
494 current_pds: None,
495 }
496 }
497}
498
499impl<'a> Iterator for ExtRegistryIter<'_, 'a> {
500 type Item = crate::Result<ExtIterItem<'a>>;
501
502 fn next(&mut self) -> Option<Self::Item> {
503 let (tag, full) = match crate::descriptors::any::next_loop_entry(
504 self.bytes,
505 &mut self.pos,
506 &mut self.fused,
507 )? {
508 Ok(v) => v,
509 Err(e) => return Some(Err(e)),
510 };
511
512 update_pds(&mut self.current_pds, tag, full);
513
514 let len = full.len() - 2;
515 if tag == crate::descriptors::extension::TAG && len >= 1 {
516 let tag_extension = full[2];
517 if self.ext_reg.has_custom(tag_extension) {
518 return Some(match self.ext_reg.parse_body(tag_extension, &full[3..]) {
519 Ok(super::extension::registry::RegisteredExtension::Custom {
520 tag_extension,
521 value,
522 }) => Ok(ExtIterItem::CustomExtension {
523 tag_extension,
524 value,
525 }),
526 Ok(super::extension::registry::RegisteredExtension::Builtin(d)) => {
527 Ok(ExtIterItem::Descriptor(AnyDescriptor::Extension(d)))
528 }
529 Err(e) => Err(e),
530 });
531 }
532 }
533
534 Some(
535 dispatch_entry(self.desc_reg, self.current_pds, tag, full).map(ExtIterItem::Descriptor),
536 )
537 }
538}
539
540impl core::iter::FusedIterator for ExtRegistryIter<'_, '_> {}
541
542#[cfg(test)]
543mod tests {
544 use super::*;
545 use crate::descriptors::private_data_specifier;
546 use crate::descriptors::private_data_specifier::{PDS_EACEM, PDS_NORDIG};
547 use crate::traits::DescriptorDef;
548
549 #[test]
550 fn pds_constants_match_the_register() {
551 assert_eq!(
554 private_data_specifier::private_data_specifier_name(PDS_EACEM),
555 Some("EACEM/EICTA")
556 );
557 assert_eq!(
558 private_data_specifier::private_data_specifier_name(PDS_NORDIG),
559 Some("NorDig")
560 );
561 }
562
563 #[derive(Debug, PartialEq, Eq)]
564 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
565 struct PdsEacem {
566 v: u8,
567 }
568
569 impl<'a> broadcast_common::Parse<'a> for PdsEacem {
570 type Error = crate::error::Error;
571 fn parse(bytes: &'a [u8]) -> crate::Result<Self> {
572 if bytes.len() < 3 {
573 return Err(crate::error::Error::BufferTooShort {
574 need: 3,
575 have: bytes.len(),
576 what: "PdsEacem",
577 });
578 }
579 Ok(Self { v: bytes[2] })
580 }
581 }
582
583 impl DescriptorDef<'_> for PdsEacem {
584 const TAG: u8 = 0x83;
585 const NAME: &'static str = "PDS_EACEM";
586 }
587
588 #[derive(Debug, PartialEq, Eq)]
589 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
590 struct PdsNordig {
591 w: u8,
592 }
593
594 impl<'a> broadcast_common::Parse<'a> for PdsNordig {
595 type Error = crate::error::Error;
596 fn parse(bytes: &'a [u8]) -> crate::Result<Self> {
597 if bytes.len() < 3 {
598 return Err(crate::error::Error::BufferTooShort {
599 need: 3,
600 have: bytes.len(),
601 what: "PdsNordig",
602 });
603 }
604 Ok(Self { w: bytes[2] })
605 }
606 }
607
608 impl DescriptorDef<'_> for PdsNordig {
609 const TAG: u8 = 0x83;
610 const NAME: &'static str = "PDS_NORDIG";
611 }
612
613 #[derive(Debug, PartialEq, Eq)]
614 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
615 struct PdsAgnostic {
616 z: u8,
617 }
618
619 impl<'a> broadcast_common::Parse<'a> for PdsAgnostic {
620 type Error = crate::error::Error;
621 fn parse(bytes: &'a [u8]) -> crate::Result<Self> {
622 if bytes.len() < 3 {
623 return Err(crate::error::Error::BufferTooShort {
624 need: 3,
625 have: bytes.len(),
626 what: "PdsAgnostic",
627 });
628 }
629 Ok(Self { z: bytes[2] })
630 }
631 }
632
633 impl DescriptorDef<'_> for PdsAgnostic {
634 const TAG: u8 = 0x84;
635 const NAME: &'static str = "PDS_AGNOSTIC";
636 }
637
638 fn pds_descriptor(pds: u32) -> Vec<u8> {
639 let mut v = vec![private_data_specifier::TAG, 4];
640 v.extend_from_slice(&pds.to_be_bytes());
641 v
642 }
643
644 #[test]
645 fn pds_scoped_same_tag_resolves_by_pds() {
646 let mut reg = DescriptorRegistry::new();
647 reg.register_for_pds::<PdsEacem>(PDS_EACEM);
648 reg.register_for_pds::<PdsNordig>(PDS_NORDIG);
649
650 let mut bytes = Vec::new();
651 bytes.extend_from_slice(&pds_descriptor(PDS_EACEM));
652 bytes.extend_from_slice(&[0x83, 0x01, 0xAA]);
653
654 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
655 assert_eq!(items.len(), 2);
656 assert!(matches!(items[0], AnyDescriptor::PrivateDataSpecifier(_)));
657 match &items[1] {
658 AnyDescriptor::Other { tag, value } => {
659 assert_eq!(*tag, 0x83);
660 let c = value.downcast_ref::<PdsEacem>().unwrap();
661 assert_eq!(c.v, 0xAA);
662 }
663 other => panic!("expected Other (PdsEacem), got {other:?}"),
664 }
665
666 let mut bytes2 = Vec::new();
667 bytes2.extend_from_slice(&pds_descriptor(PDS_NORDIG));
668 bytes2.extend_from_slice(&[0x83, 0x01, 0xBB]);
669
670 let items2: Vec<_> = reg.parse_loop(&bytes2).collect::<Result<_, _>>().unwrap();
671 match &items2[1] {
672 AnyDescriptor::Other { tag, value } => {
673 assert_eq!(*tag, 0x83);
674 let c = value.downcast_ref::<PdsNordig>().unwrap();
675 assert_eq!(c.w, 0xBB);
676 }
677 other => panic!("expected Other (PdsNordig), got {other:?}"),
678 }
679 }
680
681 #[test]
682 fn pds_scoped_does_not_match_wrong_pds() {
683 let mut reg = DescriptorRegistry::new();
684 reg.register_for_pds::<PdsEacem>(PDS_EACEM);
685
686 let mut bytes = Vec::new();
687 bytes.extend_from_slice(&pds_descriptor(PDS_NORDIG));
688 bytes.extend_from_slice(&[0x83, 0x01, 0xCC]);
689
690 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
691 assert_eq!(items.len(), 2);
692 assert!(matches!(items[0], AnyDescriptor::PrivateDataSpecifier(_)));
693 match &items[1] {
694 AnyDescriptor::Unknown { tag, .. } => assert_eq!(*tag, 0x83),
695 other => panic!("expected Unknown (wrong PDS), got {other:?}"),
696 }
697 }
698
699 #[test]
700 fn pds_agnostic_matches_without_pds() {
701 let mut reg = DescriptorRegistry::new();
702 reg.register::<PdsAgnostic>();
703
704 let bytes = [0x84, 0x01, 0xDD];
705 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
706 assert_eq!(items.len(), 1);
707 match &items[0] {
708 AnyDescriptor::Other { tag, value } => {
709 assert_eq!(*tag, 0x84);
710 let c = value.downcast_ref::<PdsAgnostic>().unwrap();
711 assert_eq!(c.z, 0xDD);
712 }
713 other => panic!("expected Other, got {other:?}"),
714 }
715 }
716
717 #[test]
718 fn pds_scoped_takes_precedence_over_agnostic() {
719 #[derive(Debug, PartialEq, Eq)]
723 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
724 struct Agnostic83 {
725 a: u8,
726 }
727
728 impl<'a> broadcast_common::Parse<'a> for Agnostic83 {
729 type Error = crate::error::Error;
730 fn parse(bytes: &'a [u8]) -> crate::Result<Self> {
731 if bytes.len() < 3 {
732 return Err(crate::error::Error::BufferTooShort {
733 need: 3,
734 have: bytes.len(),
735 what: "Agnostic83",
736 });
737 }
738 Ok(Self { a: bytes[2] })
739 }
740 }
741
742 impl DescriptorDef<'_> for Agnostic83 {
743 const TAG: u8 = 0x83;
744 const NAME: &'static str = "AGNOSTIC_83";
745 }
746
747 let mut reg = DescriptorRegistry::new();
748 reg.register::<Agnostic83>();
749 reg.register_for_pds::<PdsEacem>(PDS_EACEM);
750
751 let items: Vec<_> = reg
753 .parse_loop(&[0x83, 0x01, 0xEE])
754 .collect::<Result<_, _>>()
755 .unwrap();
756 match &items[0] {
757 AnyDescriptor::Other { value, .. } => {
758 assert!(value.downcast_ref::<Agnostic83>().is_some());
759 assert!(value.downcast_ref::<PdsEacem>().is_none());
760 }
761 other => panic!("expected Other, got {other:?}"),
762 }
763
764 let mut bytes = Vec::new();
766 bytes.extend_from_slice(&pds_descriptor(PDS_EACEM));
767 bytes.extend_from_slice(&[0x83, 0x01, 0xFF]);
768 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
769 match &items[1] {
770 AnyDescriptor::Other { value, .. } => {
771 assert!(value.downcast_ref::<PdsEacem>().is_some());
772 assert!(value.downcast_ref::<Agnostic83>().is_none());
773 }
774 other => panic!("expected Other, got {other:?}"),
775 }
776 }
777
778 fn logical_channel_descriptor(service_id: u16, visible: bool, lcn: u16) -> Vec<u8> {
779 let mut v = vec![crate::descriptors::logical_channel::TAG, 4];
781 v.extend_from_slice(&service_id.to_be_bytes());
782 let flags = (u8::from(visible) << 7) | ((lcn >> 8) as u8 & 0x03);
783 v.push(flags);
784 v.push((lcn & 0xFF) as u8);
785 v
786 }
787
788 #[test]
789 fn logical_channel_pds_scoped_matches_correct_pds() {
790 let mut reg = DescriptorRegistry::new();
791 reg.with_logical_channel_for_pds(PDS_EACEM);
792
793 let mut bytes = Vec::new();
794 bytes.extend_from_slice(&pds_descriptor(PDS_EACEM));
795 bytes.extend_from_slice(&logical_channel_descriptor(0x1234, true, 101));
796
797 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
798 assert_eq!(items.len(), 2);
799 assert!(matches!(items[0], AnyDescriptor::PrivateDataSpecifier(_)));
800 match &items[1] {
801 AnyDescriptor::LogicalChannel(lc) => {
802 assert_eq!(lc.entries.len(), 1);
803 assert_eq!(lc.entries[0].service_id, 0x1234);
804 assert!(lc.entries[0].visible_service);
805 assert_eq!(lc.entries[0].logical_channel_number, 101);
806 }
807 other => panic!("expected LogicalChannel, got {other:?}"),
808 }
809 }
810
811 #[test]
812 fn logical_channel_pds_scoped_rejects_no_pds() {
813 let mut reg = DescriptorRegistry::new();
814 reg.with_logical_channel_for_pds(PDS_EACEM);
815
816 let bytes = logical_channel_descriptor(0x1234, true, 101);
817 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
818 assert_eq!(items.len(), 1);
819 assert!(matches!(
820 &items[0],
821 AnyDescriptor::Unknown { tag: 0x83, .. }
822 ));
823 }
824
825 #[test]
826 fn logical_channel_pds_scoped_rejects_wrong_pds() {
827 let mut reg = DescriptorRegistry::new();
828 reg.with_logical_channel_for_pds(PDS_EACEM);
829
830 let mut bytes = Vec::new();
831 bytes.extend_from_slice(&pds_descriptor(PDS_NORDIG));
832 bytes.extend_from_slice(&logical_channel_descriptor(0x1234, true, 101));
833
834 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
835 assert_eq!(items.len(), 2);
836 assert!(matches!(items[0], AnyDescriptor::PrivateDataSpecifier(_)));
837 assert!(matches!(
838 &items[1],
839 AnyDescriptor::Unknown { tag: 0x83, .. }
840 ));
841 }
842
843 #[test]
844 fn logical_channel_pds_scoped_multiple_pds() {
845 let mut reg = DescriptorRegistry::new();
846 reg.with_logical_channel_for_pds(PDS_EACEM);
847 reg.with_logical_channel_for_pds(PDS_NORDIG);
848
849 let mut bytes = Vec::new();
851 bytes.extend_from_slice(&pds_descriptor(PDS_EACEM));
852 bytes.extend_from_slice(&logical_channel_descriptor(0x0001, true, 1));
853 let items: Vec<_> = reg.parse_loop(&bytes).collect::<Result<_, _>>().unwrap();
854 assert!(matches!(&items[1], AnyDescriptor::LogicalChannel(_)));
855
856 let mut bytes2 = Vec::new();
858 bytes2.extend_from_slice(&pds_descriptor(PDS_NORDIG));
859 bytes2.extend_from_slice(&logical_channel_descriptor(0x0002, false, 2));
860 let items2: Vec<_> = reg.parse_loop(&bytes2).collect::<Result<_, _>>().unwrap();
861 assert!(matches!(&items2[1], AnyDescriptor::LogicalChannel(_)));
862 }
863
864 #[test]
865 fn iter_with_extensions_surfaces_custom_extension() {
866 use crate::descriptors::any::{AnyDescriptor, DescriptorLoop};
867 use crate::descriptors::extension::registry::ExtensionRegistry;
868
869 #[derive(Debug, PartialEq, Eq)]
870 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
871 struct MyCustomExt {
872 payload: Vec<u8>,
873 }
874
875 impl<'a> broadcast_common::Parse<'a> for MyCustomExt {
876 type Error = crate::error::Error;
877 fn parse(sel: &'a [u8]) -> crate::Result<Self> {
878 Ok(Self {
879 payload: sel.to_vec(),
880 })
881 }
882 }
883
884 impl crate::descriptors::extension::ExtensionBodyDef<'_> for MyCustomExt {
885 const TAG_EXTENSION: u8 = 0x42;
886 const NAME: &'static str = "MY_CUSTOM_EXT";
887 }
888
889 let mut ext_reg = ExtensionRegistry::new();
890 ext_reg.register::<MyCustomExt>();
891
892 let desc_reg = DescriptorRegistry::new();
893
894 let mut loop_bytes = vec![
896 0x4D, 0x07, b'e', b'n', b'g', 0x02, b'H', b'i', 0x00, ];
898 loop_bytes.extend_from_slice(&[0x7F, 0x03, 0x42, 0xAB, 0xCD]);
900
901 let dl = DescriptorLoop::new(&loop_bytes);
902 let items: Vec<_> = dl
903 .iter_with_extensions(&desc_reg, &ext_reg)
904 .collect::<Result<_, _>>()
905 .unwrap();
906 assert_eq!(items.len(), 2);
907 assert!(matches!(
909 &items[0],
910 ExtIterItem::Descriptor(AnyDescriptor::ShortEvent(_))
911 ));
912 match &items[1] {
914 ExtIterItem::CustomExtension {
915 tag_extension,
916 value,
917 } => {
918 assert_eq!(*tag_extension, 0x42);
919 let concrete = value.downcast_ref::<MyCustomExt>().unwrap();
920 assert_eq!(concrete.payload, &[0xAB, 0xCD]);
921 }
922 other => panic!("expected CustomExtension, got {other:?}"),
923 }
924 }
925}