1use crate::error::{Error, Result};
8use dvb_common::{Parse, Serialize};
9
10pub const TABLE_ID: u8 = 0x7E;
12pub const PID: u16 = 0x001E;
14
15const HEADER_LEN: usize = 3;
16const BODY_LEN: usize = 1;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize))]
22pub struct DitSection {
23 pub transition_flag: bool,
25}
26
27impl<'a> Parse<'a> for DitSection {
28 type Error = crate::error::Error;
29
30 fn parse(bytes: &'a [u8]) -> Result<Self> {
31 let min_len = HEADER_LEN + BODY_LEN;
32 if bytes.len() < min_len {
33 return Err(Error::BufferTooShort {
34 need: min_len,
35 have: bytes.len(),
36 what: "DitSection",
37 });
38 }
39 if bytes[0] != TABLE_ID {
40 return Err(Error::UnexpectedTableId {
41 table_id: bytes[0],
42 what: "DitSection",
43 expected: &[TABLE_ID],
44 });
45 }
46 let section_length = ((bytes[1] & 0x0F) as usize) << 8 | bytes[2] as usize;
47 if section_length != BODY_LEN {
48 return Err(Error::SectionLengthOverflow {
49 declared: section_length,
50 available: BODY_LEN,
51 });
52 }
53 let transition_flag = bytes[3] & 0x80 != 0;
55 Ok(DitSection { transition_flag })
56 }
57}
58
59impl Serialize for DitSection {
60 type Error = crate::error::Error;
61
62 fn serialized_len(&self) -> usize {
63 HEADER_LEN + BODY_LEN
64 }
65
66 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
67 let len = self.serialized_len();
68 if buf.len() < len {
69 return Err(Error::OutputBufferTooSmall {
70 need: len,
71 have: buf.len(),
72 });
73 }
74 buf[0] = TABLE_ID;
75 buf[1] = super::SECTION_B1_FLAGS_SHORT | ((BODY_LEN >> 8) as u8 & 0x0F);
78 buf[2] = (BODY_LEN & 0xFF) as u8;
79 buf[3] = (u8::from(self.transition_flag) << 7) | 0x7F;
81 Ok(len)
82 }
83}
84impl<'a> crate::traits::TableDef<'a> for DitSection {
85 const TABLE_ID_RANGES: &'static [(u8, u8)] = &[(TABLE_ID, TABLE_ID)];
86 const NAME: &'static str = "DISCONTINUITY_INFORMATION";
87}
88
89#[cfg(test)]
90mod tests {
91 use super::*;
92
93 #[test]
94 fn parse_transition_flag_set() {
95 let bytes = [TABLE_ID, 0x70, 0x01, 0x80];
97 let dit = DitSection::parse(&bytes).unwrap();
98 assert!(dit.transition_flag);
99 }
100
101 #[test]
102 fn parse_transition_flag_clear() {
103 let bytes = [TABLE_ID, 0x70, 0x01, 0x7F];
104 let dit = DitSection::parse(&bytes).unwrap();
105 assert!(!dit.transition_flag);
106 }
107
108 #[test]
109 fn parse_rejects_wrong_tag() {
110 let bytes = [0x7F, 0x70, 0x01, 0x80];
111 assert!(matches!(
112 DitSection::parse(&bytes).unwrap_err(),
113 Error::UnexpectedTableId { table_id: 0x7F, .. }
114 ));
115 }
116
117 #[test]
118 fn parse_rejects_wrong_section_length() {
119 let bytes = [TABLE_ID, 0x70, 0x02, 0x80, 0x00];
120 assert!(matches!(
121 DitSection::parse(&bytes).unwrap_err(),
122 Error::SectionLengthOverflow { .. }
123 ));
124 }
125
126 #[test]
127 fn parse_rejects_short_buffer() {
128 let bytes = [TABLE_ID, 0x70];
129 assert!(matches!(
130 DitSection::parse(&bytes).unwrap_err(),
131 Error::BufferTooShort { .. }
132 ));
133 }
134
135 #[test]
136 fn serialize_round_trip_set() {
137 let dit = DitSection {
138 transition_flag: true,
139 };
140 let mut buf = vec![0u8; dit.serialized_len()];
141 dit.serialize_into(&mut buf).unwrap();
142 assert_eq!(buf, [TABLE_ID, 0x70, 0x01, 0xFF]);
143 assert_eq!(DitSection::parse(&buf).unwrap(), dit);
144 }
145
146 #[test]
147 fn serialize_round_trip_clear() {
148 let dit = DitSection {
149 transition_flag: false,
150 };
151 let mut buf = vec![0u8; dit.serialized_len()];
152 dit.serialize_into(&mut buf).unwrap();
153 assert_eq!(buf, [TABLE_ID, 0x70, 0x01, 0x7F]);
154 assert_eq!(DitSection::parse(&buf).unwrap(), dit);
155 }
156
157 #[test]
158 fn serialize_into_too_small_buffer() {
159 let dit = DitSection {
160 transition_flag: false,
161 };
162 let mut buf = [0u8; 3];
163 assert!(matches!(
164 dit.serialize_into(&mut buf).unwrap_err(),
165 Error::OutputBufferTooSmall { .. }
166 ));
167 }
168
169 #[test]
170 fn serialized_len_is_four() {
171 assert_eq!(
172 DitSection {
173 transition_flag: false
174 }
175 .serialized_len(),
176 4
177 );
178 }
179
180 #[cfg(feature = "serde")]
181 #[test]
182 fn serde_json_serializes_fields() {
183 let dit = DitSection {
185 transition_flag: true,
186 };
187 let v = serde_json::to_value(dit).unwrap();
188 assert_eq!(v["transition_flag"], true);
189 }
190
191 #[test]
192 fn table_trait_constants() {
193 assert_eq!(TABLE_ID, 0x7E);
194 assert_eq!(PID, 0x001E);
195 }
196}