Skip to main content

dvb_si/
table_id.rs

1//! `TableId` enum: typed `table_id` byte values.
2//!
3//! Source: ETSI EN 300 468 §5.1.3 Table 2 plus ISO/IEC 13818-1 for MPEG tables.
4
5use num_enum::TryFromPrimitive;
6
7/// Typed `table_id` enumeration.
8///
9/// Variants use Rust CamelCase in 4.0. They intentionally do not carry the
10/// `Section` suffix; that suffix belongs to parser types such as
11/// [`crate::tables::nit::NitSection`].
12///
13/// Tables that occupy a range of values (EIT schedule 0x50..=0x5F and 0x60..=0x6F)
14/// are not listed as enum variants; see [`Self::eit_schedule_actual_segment`] and
15/// [`Self::eit_schedule_other_segment`] instead.
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, TryFromPrimitive)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize))]
18#[repr(u8)]
19#[allow(missing_docs)]
20#[non_exhaustive]
21#[rustfmt::skip] // deliberate column-aligned discriminants
22pub enum TableId {
23    // ── MPEG-2 tables ──────────────────────────────────────────────────────
24    Pat                        = 0x00,
25    Cat                        = 0x01,
26    Pmt                        = 0x02,
27    TransportStreamDescription = 0x03,
28    Iso14496SceneDescription   = 0x04,
29    Iso14496ObjectDescriptor   = 0x05,
30    Metadata                   = 0x06,
31    Iso14496                   = 0x08,
32    GreenAccessUnit            = 0x09,
33
34    // ── DVB SI tables ──────────────────────────────────────────────────────
35    NetworkInformationActual   = 0x40,
36    NetworkInformationOther    = 0x41,
37    ServiceDescriptionActual   = 0x42,
38    ServiceDescriptionOther    = 0x46,
39    BouquetAssociation         = 0x4A,
40    UpdateNotification         = 0x4B,
41    IpMacNotification          = 0x4C,
42    SatelliteAccess            = 0x4D,
43    EventInformationPfActual   = 0x4E,
44    EventInformationPfOther    = 0x4F,
45
46    // EIT schedule actual covers 0x50..=0x5F (16 segments).
47    // EIT schedule other covers 0x60..=0x6F.
48
49    TimeAndDate                = 0x70,
50    RunningStatus              = 0x71,
51    Stuffing                   = 0x72,
52    TimeOffset                 = 0x73,
53    ApplicationInformation     = 0x74,
54    Container                  = 0x75,
55    RelatedContent             = 0x76,
56    ContentIdentifier          = 0x77,
57    MpeFec                     = 0x78,
58    ResolutionNotification     = 0x79,
59    MpeIfec                    = 0x7A,
60    ProtectionMessage          = 0x7B,
61    DownloadableFontInfo       = 0x7C,
62    DiscontinuityInformation   = 0x7E,
63    SelectionInformation       = 0x7F,
64}
65
66impl TableId {
67    /// If `v` is an EIT-schedule-actual `table_id` (0x50..=0x5F), return its
68    /// segment index 0..=15. Otherwise `None`.
69    #[must_use]
70    pub const fn eit_schedule_actual_segment(v: u8) -> Option<u8> {
71        if v >= 0x50 && v <= 0x5F {
72            Some(v - 0x50)
73        } else {
74            None
75        }
76    }
77
78    /// If `v` is an EIT-schedule-other `table_id` (0x60..=0x6F), return its
79    /// segment index 0..=15. Otherwise `None`.
80    #[must_use]
81    pub const fn eit_schedule_other_segment(v: u8) -> Option<u8> {
82        if v >= 0x60 && v <= 0x6F {
83            Some(v - 0x60)
84        } else {
85            None
86        }
87    }
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn known_values_round_trip() {
96        for id in [
97            TableId::Pat,
98            TableId::NetworkInformationActual,
99            TableId::ServiceDescriptionActual,
100            TableId::EventInformationPfActual,
101            TableId::TimeAndDate,
102            TableId::SelectionInformation,
103        ] {
104            let byte = id as u8;
105            assert_eq!(TableId::try_from(byte), Ok(id));
106        }
107    }
108
109    #[test]
110    fn eit_schedule_actual_segment_range() {
111        assert_eq!(TableId::eit_schedule_actual_segment(0x4F), None);
112        assert_eq!(TableId::eit_schedule_actual_segment(0x50), Some(0));
113        assert_eq!(TableId::eit_schedule_actual_segment(0x5F), Some(0x0F));
114        assert_eq!(TableId::eit_schedule_actual_segment(0x60), None);
115    }
116
117    #[test]
118    fn eit_schedule_other_segment_range() {
119        assert_eq!(TableId::eit_schedule_other_segment(0x5F), None);
120        assert_eq!(TableId::eit_schedule_other_segment(0x60), Some(0));
121        assert_eq!(TableId::eit_schedule_other_segment(0x6F), Some(0x0F));
122        assert_eq!(TableId::eit_schedule_other_segment(0x70), None);
123    }
124
125    #[test]
126    fn unknown_value_rejected() {
127        assert!(TableId::try_from(0x99).is_err());
128    }
129
130    #[test]
131    fn exhaustive_byte_sweep() {
132        let mut matched = 0u16;
133        for byte in 0u8..=0xFF {
134            if let Ok(id) = TableId::try_from(byte) {
135                assert_eq!(id as u8, byte, "round-trip failed for {byte:#04x}");
136                matched += 1;
137            }
138        }
139        // 34 defined variants (29 + the five typed MPEG-2 sections 0x04/05/06/08/09)
140        assert_eq!(matched, 34, "expected 34 matched variants");
141    }
142}