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