dvb-si 12.1.0

ETSI EN 300 468 DVB Service Information parser + builder. MPEG-2 PSI included.
Documentation
//! Scoped descriptor-tag namespaces — INT / UNT / RNT (issue #1285).
//!
//! Three tables carry descriptor loops whose tag space is **not** the
//! EN 300 468 / H.222.0 one:
//!
//! | Table | Namespace owned by the table | Everything else |
//! |---|---|---|
//! | INT (0x4C) | tags `0x00..=0x3F` — ETSI EN 301 192 V1.7.1 Table 19 (clause 8.4.5.1): "MPEG descriptors in the range 0x00 to 0x3F cannot be used in the INT" | `0x40..=0xFF` has "their standard semantics as defined in ETSI EN 300 468" |
//! | UNT (0x4B) | tags `0x00..=0x3F` — ETSI TS 102 006 V1.4.1 Table 19 (clause 9.5.1), same wording | `0x40..=0xFF` is DVB-SI scope |
//! | RNT (0x79) | tags `0x40..=0x42` — ETSI TS 102 323 V1.4.1 Table 4 (clause 5.3.3.1) "override the definition of the meaning of those descriptor tags in EN 300 468, when they are used in the RNT table only" | every other tag (incl. the MPEG-range `metadata_pointer` 0x25) keeps its SI meaning |
//!
//! Each table gets its own dispatch enum + loop walker generated by
//! [`declare_scoped_namespace!`](crate::descriptors::scoped) from one
//! declarative list (the `AnyAitDescriptor` pattern): the typed variants for
//! the table's own tags, an `Si` variant carrying the SI dispatch
//! ([`AnyDescriptor`](crate::descriptors::AnyDescriptor)) for the tags the
//! spec hands back to EN 300 468, and `Unknown` for an own-namespace tag with
//! no implementation (never mis-decoded through the SI table).

/// A descriptor loop of one scoped namespace: the raw `descriptor()` bytes
/// (no enclosing 12-bit length field), borrowed from the section.
///
/// Implemented by `IntDescriptorLoop`, `UntDescriptorLoop` and
/// `RntDescriptorLoop` so the shared
/// [`TargetOperationalLoop`](crate::tables::TargetOperationalLoop) pair can
/// carry whichever namespace its table defines.
pub trait ScopedDescriptorLoop<'a>: Copy {
    /// Wrap a raw loop slice.
    fn from_raw(raw: &'a [u8]) -> Self;
    /// The raw wire bytes of the loop, verbatim.
    fn raw(&self) -> &'a [u8];
}

/// Generate a scoped namespace: dispatch enum, loop walker and loop wrapper.
///
/// The typed list must be in ascending tag order (checked by the generated
/// `macro_drift` test). `owned $pat` (optional) names the tag range the table
/// owns outright: a tag in it with no typed variant is `Unknown`, never SI.
/// Every other tag falls through to the SI dispatch.
macro_rules! declare_scoped_namespace {
    (
        $lt:lifetime;
        doc $doc:literal;
        enum $Enum:ident, iter $Iter:ident, loop $Loop:ident, parse $parse_fn:ident;
        $( @owned $owned:pat, )?
        $( $variant:ident = $tag:literal => $($path:ident)::+ $(<$plt:lifetime>)? ),+ $(,)?
    ) => {
        #[doc = $doc]
        ///
        /// serde uses external tagging with camelCase variant keys; the SI
        /// fall-through serializes as `{"si": {…}}`.
        #[derive(Debug)]
        #[cfg_attr(feature = "serde", derive(serde::Serialize))]
        #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
        #[non_exhaustive]
        pub enum $Enum<$lt> {
            $(
                #[allow(missing_docs)]
                $variant($($path)::+ $(<$plt>)?),
            )+
            /// A tag the spec hands back to EN 300 468 (DVB SI) semantics,
            /// decoded by the SI dispatch.
            Si(crate::descriptors::AnyDescriptor<$lt>),
            /// A tag in this table's own namespace with no typed
            /// implementation; `body` is the payload sans the 2-byte header.
            Unknown {
                /// The raw descriptor_tag byte.
                tag: u8,
                /// The raw payload bytes (descriptor_length bytes).
                body: &$lt [u8],
            },
        }

        $(
            impl<$lt> From<$($path)::+ $(<$plt>)?> for $Enum<$lt> {
                fn from(d: $($path)::+ $(<$plt>)?) -> Self {
                    Self::$variant(d)
                }
            }
        )+

        impl<$lt> $Enum<$lt> {
            /// Every tag the generated dispatcher routes to a namespace type
            /// (excludes the `Si` fall-through and `Unknown`), ascending.
            pub const DISPATCHED_TAGS: &'static [u8] = &[$($tag),+];

            /// Diagnostic name of the contained descriptor — the type's
            /// [`DescriptorDef::NAME`](crate::traits::DescriptorDef::NAME);
            /// the SI descriptor's own name for `Si`; `"UNKNOWN"` for `Unknown`.
            #[must_use]
            pub fn name(&self) -> &'static str {
                match self {
                    $(
                        Self::$variant(_) =>
                            <$($path)::+ as crate::traits::DescriptorDef>::NAME,
                    )+
                    Self::Si(d) => d.name(),
                    Self::Unknown { .. } => "UNKNOWN",
                }
            }

            /// Re-serialize to the full wire form (2-byte header included).
            /// `None` only for a type-erased registry value inside `Si`.
            #[doc(hidden)]
            pub fn reserialize(
                &self,
            ) -> Option<core::result::Result<alloc::vec::Vec<u8>, alloc::string::String>> {
                use broadcast_common::Serialize;
                fn go<S: Serialize>(
                    d: &S,
                ) -> core::result::Result<alloc::vec::Vec<u8>, alloc::string::String>
                where
                    S::Error: core::fmt::Debug,
                {
                    let mut out = alloc::vec![0u8; d.serialized_len()];
                    d.serialize_into(&mut out)
                        .map_err(|e| alloc::format!("{e:?}"))?;
                    Ok(out)
                }
                match self {
                    $( Self::$variant(d) => Some(go(d)), )+
                    Self::Si(d) => d.reserialize(),
                    Self::Unknown { tag, body } => Some(
                        broadcast_common::len::fit_u8(body.len(), "descriptor_length")
                            .map(|n| {
                                let mut v = alloc::vec![*tag, n];
                                v.extend_from_slice(body);
                                v
                            })
                            .map_err(|e| alloc::format!("{e:?}")),
                    ),
                }
            }

            /// Parse one full descriptor (2-byte header included) by its tag.
            pub(crate) fn dispatch(tag: u8, full: &$lt [u8]) -> crate::error::Result<Self> {
                use broadcast_common::Parse;
                match tag {
                    $(
                        $tag => <$($path)::+>::parse(full).map(Self::$variant),
                    )+
                    $( $owned => Ok(Self::Unknown { tag, body: &full[2..] }), )?
                    _ => match crate::descriptors::AnyDescriptor::dispatch(tag, full) {
                        Some(res) => res.map(Self::Si),
                        None => Ok(Self::Si(crate::descriptors::AnyDescriptor::Unknown {
                            tag,
                            body: &full[2..],
                        })),
                    },
                }
            }
        }

        /// Lazily walk a raw descriptor loop of this namespace. Never panics.
        ///
        /// Per-descriptor parse errors yield `Err` and iteration continues
        /// (`descriptor_length` bounds each entry); a truncated final header
        /// or body yields one `Err` and the iterator fuses.
        #[must_use]
        pub fn $parse_fn(bytes: &[u8]) -> $Iter<'_> {
            $Iter { bytes, pos: 0, fused: false }
        }

        /// Iterator over a raw descriptor loop of this namespace.
        #[derive(Debug, Clone)]
        pub struct $Iter<'a> {
            bytes: &'a [u8],
            pos: usize,
            fused: bool,
        }

        impl<'a> Iterator for $Iter<'a> {
            type Item = crate::error::Result<$Enum<'a>>;

            fn next(&mut self) -> Option<Self::Item> {
                let (tag, full) = match crate::descriptors::any::next_loop_entry(
                    self.bytes,
                    &mut self.pos,
                    &mut self.fused,
                )? {
                    Ok(v) => v,
                    Err(e) => return Some(Err(e)),
                };
                Some($Enum::dispatch(tag, full))
            }
        }

        impl core::iter::FusedIterator for $Iter<'_> {}

        /// A raw descriptor loop of this namespace, borrowed from the section.
        /// Zero-copy: walk it typed via `iter()`; serde serializes the typed
        /// walk (a parse error becomes `{"parseError": "…"}`, never dropped).
        #[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
        #[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
        pub struct $Loop<$lt>(&$lt [u8]);

        impl<$lt> $Loop<$lt> {
            /// Wrap a raw descriptor-loop slice (the `descriptor()` bytes only
            /// — no enclosing length field).
            #[must_use]
            pub const fn new(raw: &$lt [u8]) -> Self {
                Self(raw)
            }

            /// The raw wire bytes of the loop, verbatim.
            #[must_use]
            pub const fn raw(&self) -> &$lt [u8] {
                self.0
            }

            /// Lazily walk the loop, one typed entry per descriptor.
            #[must_use]
            pub fn iter(&self) -> $Iter<$lt> {
                $parse_fn(self.0)
            }
        }

        impl<$lt> crate::descriptors::scoped::ScopedDescriptorLoop<$lt> for $Loop<$lt> {
            fn from_raw(raw: &$lt [u8]) -> Self {
                Self(raw)
            }
            fn raw(&self) -> &$lt [u8] {
                self.0
            }
        }

        impl core::ops::Deref for $Loop<'_> {
            /// Derefs to the raw wire bytes — `len()` is a **byte count for
            /// serialization, not an entry count**.
            type Target = [u8];
            fn deref(&self) -> &[u8] {
                self.0
            }
        }

        impl<$lt> From<&$lt [u8]> for $Loop<$lt> {
            fn from(raw: &$lt [u8]) -> Self {
                Self(raw)
            }
        }

        impl core::fmt::Debug for $Loop<'_> {
            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
                write!(f, concat!(stringify!($Loop), "(<{} bytes>)"), self.0.len())
            }
        }

        impl<$lt> IntoIterator for &$Loop<$lt> {
            type Item = crate::error::Result<$Enum<$lt>>;
            type IntoIter = $Iter<$lt>;
            fn into_iter(self) -> Self::IntoIter {
                self.iter()
            }
        }

        #[cfg(feature = "serde")]
        impl serde::Serialize for $Loop<'_> {
            fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
                struct Entry<'a>(crate::error::Result<$Enum<'a>>);
                impl serde::Serialize for Entry<'_> {
                    fn serialize<S: serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
                        match &self.0 {
                            Ok(d) => d.serialize(s),
                            Err(e) => {
                                use serde::ser::SerializeMap;
                                let mut m = s.serialize_map(Some(1))?;
                                m.serialize_entry(
                                    "parseError",
                                    &alloc::string::ToString::to_string(e),
                                )?;
                                m.end()
                            }
                        }
                    }
                }
                s.collect_seq(self.iter().map(Entry))
            }
        }

        #[cfg(test)]
        mod macro_drift {
            #[test]
            fn tag_literals_match_descriptor_def_and_are_ascending() {
                use crate::traits::DescriptorDef;
                $(
                    assert_eq!(
                        $tag,
                        <$($path)::+ as DescriptorDef>::TAG,
                        concat!("tag literal drift for ", stringify!($variant)),
                    );
                    assert!(
                        !<$($path)::+ as DescriptorDef>::NAME.is_empty(),
                        concat!("empty NAME for ", stringify!($variant)),
                    );
                )+
                let tags = super::$Enum::DISPATCHED_TAGS;
                assert!(
                    tags.windows(2).all(|w| w[0] < w[1]),
                    "declare list must be in strictly ascending tag order: {tags:02X?}"
                );
            }
        }
    };
}

pub(crate) use declare_scoped_namespace;