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