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#[rustfmt::skip] // deliberate column-aligned discriminants
21pub enum TableId {
22    // ── MPEG-2 tables ──────────────────────────────────────────────────────
23    Pat                        = 0x00,
24    Cat                        = 0x01,
25    Pmt                        = 0x02,
26    TransportStreamDescription = 0x03,
27
28    // ── DVB SI tables ──────────────────────────────────────────────────────
29    NetworkInformationActual   = 0x40,
30    NetworkInformationOther    = 0x41,
31    ServiceDescriptionActual   = 0x42,
32    ServiceDescriptionOther    = 0x46,
33    BouquetAssociation         = 0x4A,
34    UpdateNotification         = 0x4B,
35    IpMacNotification          = 0x4C,
36    SatelliteAccess            = 0x4D,
37    EventInformationPfActual   = 0x4E,
38    EventInformationPfOther    = 0x4F,
39
40    // EIT schedule actual covers 0x50..=0x5F (16 segments).
41    // EIT schedule other covers 0x60..=0x6F.
42
43    TimeAndDate                = 0x70,
44    RunningStatus              = 0x71,
45    Stuffing                   = 0x72,
46    TimeOffset                 = 0x73,
47    ApplicationInformation     = 0x74,
48    Container                  = 0x75,
49    RelatedContent             = 0x76,
50    ContentIdentifier          = 0x77,
51    MpeFec                     = 0x78,
52    ResolutionNotification     = 0x79,
53    MpeIfec                    = 0x7A,
54    ProtectionMessage          = 0x7B,
55    DownloadableFontInfo       = 0x7C,
56    DiscontinuityInformation   = 0x7E,
57    SelectionInformation       = 0x7F,
58}
59
60impl TableId {
61    /// If `v` is an EIT-schedule-actual `table_id` (0x50..=0x5F), return its
62    /// segment index 0..=15. Otherwise `None`.
63    #[must_use]
64    pub const fn eit_schedule_actual_segment(v: u8) -> Option<u8> {
65        if v >= 0x50 && v <= 0x5F {
66            Some(v - 0x50)
67        } else {
68            None
69        }
70    }
71
72    /// If `v` is an EIT-schedule-other `table_id` (0x60..=0x6F), return its
73    /// segment index 0..=15. Otherwise `None`.
74    #[must_use]
75    pub const fn eit_schedule_other_segment(v: u8) -> Option<u8> {
76        if v >= 0x60 && v <= 0x6F {
77            Some(v - 0x60)
78        } else {
79            None
80        }
81    }
82}
83
84#[cfg(test)]
85mod tests {
86    use super::*;
87
88    #[test]
89    fn known_values_round_trip() {
90        for id in [
91            TableId::Pat,
92            TableId::NetworkInformationActual,
93            TableId::ServiceDescriptionActual,
94            TableId::EventInformationPfActual,
95            TableId::TimeAndDate,
96            TableId::SelectionInformation,
97        ] {
98            let byte = id as u8;
99            assert_eq!(TableId::try_from(byte), Ok(id));
100        }
101    }
102
103    #[test]
104    fn eit_schedule_actual_segment_range() {
105        assert_eq!(TableId::eit_schedule_actual_segment(0x4F), None);
106        assert_eq!(TableId::eit_schedule_actual_segment(0x50), Some(0));
107        assert_eq!(TableId::eit_schedule_actual_segment(0x5F), Some(0x0F));
108        assert_eq!(TableId::eit_schedule_actual_segment(0x60), None);
109    }
110
111    #[test]
112    fn eit_schedule_other_segment_range() {
113        assert_eq!(TableId::eit_schedule_other_segment(0x5F), None);
114        assert_eq!(TableId::eit_schedule_other_segment(0x60), Some(0));
115        assert_eq!(TableId::eit_schedule_other_segment(0x6F), Some(0x0F));
116        assert_eq!(TableId::eit_schedule_other_segment(0x70), None);
117    }
118
119    #[test]
120    fn unknown_value_rejected() {
121        assert!(TableId::try_from(0x99).is_err());
122    }
123
124    #[test]
125    fn exhaustive_byte_sweep() {
126        let mut matched = 0u16;
127        for byte in 0u8..=0xFF {
128            if let Ok(id) = TableId::try_from(byte) {
129                assert_eq!(id as u8, byte, "round-trip failed for {byte:#04x}");
130                matched += 1;
131            }
132        }
133        // 29 defined variants (added ProtectionMessage 0x7B, DownloadableFontInfo 0x7C)
134        assert_eq!(matched, 29, "expected 29 matched variants");
135    }
136}