1use super::descriptor_body;
10use crate::error::{Error, Result};
11use dvb_common::{Parse, Serialize};
12
13pub const TAG: u8 = 0x75;
15const HEADER_LEN: usize = 2;
16const ENTRY_LEN: usize = 3;
17
18const RUNNING_STATUS_MAX: u8 = 0x07;
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize))]
27#[non_exhaustive]
28pub enum TvaRunningStatus {
29 Reserved,
31 NotYetRunning,
33 StartsShortly,
35 Paused,
37 Running,
39 Cancelled,
41 Completed,
43 Unallocated(u8),
45}
46
47impl TvaRunningStatus {
48 #[must_use]
49 pub fn from_u8(v: u8) -> Self {
52 match v {
53 0 => Self::Reserved,
54 1 => Self::NotYetRunning,
55 2 => Self::StartsShortly,
56 3 => Self::Paused,
57 4 => Self::Running,
58 5 => Self::Cancelled,
59 6 => Self::Completed,
60 v => Self::Unallocated(v),
61 }
62 }
63
64 #[must_use]
65 pub fn to_u8(self) -> u8 {
67 match self {
68 Self::Reserved => 0,
69 Self::NotYetRunning => 1,
70 Self::StartsShortly => 2,
71 Self::Paused => 3,
72 Self::Running => 4,
73 Self::Cancelled => 5,
74 Self::Completed => 6,
75 Self::Unallocated(v) => v,
76 }
77 }
78
79 #[must_use]
80 pub fn name(self) -> &'static str {
82 match self {
83 Self::Reserved => "reserved",
84 Self::NotYetRunning => "not yet running",
85 Self::StartsShortly => "starts shortly",
86 Self::Paused => "paused",
87 Self::Running => "running",
88 Self::Cancelled => "cancelled",
89 Self::Completed => "completed",
90 Self::Unallocated(_) => "unallocated",
91 }
92 }
93}
94dvb_common::impl_spec_display!(TvaRunningStatus, Unallocated);
95
96#[derive(Debug, Clone, PartialEq, Eq)]
98#[cfg_attr(feature = "serde", derive(serde::Serialize))]
99pub struct TvaIdEntry {
100 pub tva_id: u16,
102 pub running_status: TvaRunningStatus,
104}
105
106#[derive(Debug, Clone, PartialEq, Eq)]
108#[cfg_attr(feature = "serde", derive(serde::Serialize))]
109pub struct TvaIdDescriptor {
110 pub entries: Vec<TvaIdEntry>,
112}
113
114impl<'a> Parse<'a> for TvaIdDescriptor {
115 type Error = crate::error::Error;
116 fn parse(bytes: &'a [u8]) -> Result<Self> {
117 let body = descriptor_body(
118 bytes,
119 TAG,
120 "TvaIdDescriptor",
121 "unexpected tag for TVA_id_descriptor",
122 )?;
123 if body.len() % ENTRY_LEN != 0 {
124 return Err(Error::InvalidDescriptor {
125 tag: TAG,
126 reason: "TVA_id_descriptor length must be a multiple of 3",
127 });
128 }
129 let mut entries = Vec::with_capacity(body.len() / ENTRY_LEN);
130 for chunk in body.chunks_exact(ENTRY_LEN) {
131 let tva_id = u16::from_be_bytes([chunk[0], chunk[1]]);
132 let running_status = TvaRunningStatus::from_u8(chunk[2] & RUNNING_STATUS_MAX);
133 entries.push(TvaIdEntry {
134 tva_id,
135 running_status,
136 });
137 }
138 Ok(Self { entries })
139 }
140}
141
142impl Serialize for TvaIdDescriptor {
143 type Error = crate::error::Error;
144 fn serialized_len(&self) -> usize {
145 HEADER_LEN + self.entries.len() * ENTRY_LEN
146 }
147
148 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
149 for e in &self.entries {
150 if e.running_status.to_u8() > RUNNING_STATUS_MAX {
151 return Err(Error::InvalidDescriptor {
152 tag: TAG,
153 reason: "running_status exceeds 3 bits",
154 });
155 }
156 }
157 if self.entries.len() * ENTRY_LEN > u8::MAX as usize {
158 return Err(Error::InvalidDescriptor {
159 tag: TAG,
160 reason: "TVA_id_descriptor body exceeds 255 bytes",
161 });
162 }
163 let len = self.serialized_len();
164 if buf.len() < len {
165 return Err(Error::OutputBufferTooSmall {
166 need: len,
167 have: buf.len(),
168 });
169 }
170 buf[0] = TAG;
171 buf[1] = (self.entries.len() * ENTRY_LEN) as u8;
172 let mut pos = HEADER_LEN;
173 for e in &self.entries {
174 buf[pos..pos + 2].copy_from_slice(&e.tva_id.to_be_bytes());
175 buf[pos + 2] = 0xF8 | (e.running_status.to_u8() & RUNNING_STATUS_MAX);
177 pos += ENTRY_LEN;
178 }
179 Ok(len)
180 }
181}
182impl<'a> crate::traits::DescriptorDef<'a> for TvaIdDescriptor {
183 const TAG: u8 = TAG;
184 const NAME: &'static str = "TVA_ID";
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn parse_single_entry() {
193 let bytes = [TAG, 3, 0x12, 0x34, 0xFC];
195 let d = TvaIdDescriptor::parse(&bytes).unwrap();
196 assert_eq!(d.entries.len(), 1);
197 assert_eq!(d.entries[0].tva_id, 0x1234);
198 assert_eq!(d.entries[0].running_status, TvaRunningStatus::Running);
199 }
200
201 #[test]
202 fn parse_multiple_entries() {
203 let bytes = [TAG, 6, 0x00, 0x01, 0x01, 0xAB, 0xCD, 0x06];
204 let d = TvaIdDescriptor::parse(&bytes).unwrap();
205 assert_eq!(d.entries.len(), 2);
206 assert_eq!(d.entries[0].tva_id, 0x0001);
207 assert_eq!(d.entries[0].running_status, TvaRunningStatus::NotYetRunning);
208 assert_eq!(d.entries[1].tva_id, 0xABCD);
209 assert_eq!(d.entries[1].running_status, TvaRunningStatus::Completed);
210 }
211
212 #[test]
213 fn parse_ignores_reserved_bits() {
214 let bytes = [TAG, 3, 0x00, 0x00, 0xFF];
215 let d = TvaIdDescriptor::parse(&bytes).unwrap();
216 assert_eq!(
217 d.entries[0].running_status,
218 TvaRunningStatus::Unallocated(0x07)
219 );
220 }
221
222 #[test]
223 fn parse_rejects_wrong_tag() {
224 assert!(matches!(
225 TvaIdDescriptor::parse(&[0x74, 0]).unwrap_err(),
226 Error::InvalidDescriptor { tag: 0x74, .. }
227 ));
228 }
229
230 #[test]
231 fn parse_rejects_length_not_multiple_of_3() {
232 let bytes = [TAG, 2, 0, 0];
233 assert!(matches!(
234 TvaIdDescriptor::parse(&bytes).unwrap_err(),
235 Error::InvalidDescriptor { .. }
236 ));
237 }
238
239 #[test]
240 fn empty_descriptor_valid() {
241 let bytes = [TAG, 0];
242 let d = TvaIdDescriptor::parse(&bytes).unwrap();
243 assert!(d.entries.is_empty());
244 }
245
246 #[test]
247 fn serialize_round_trip() {
248 let d = TvaIdDescriptor {
249 entries: vec![
250 TvaIdEntry {
251 tva_id: 0x1000,
252 running_status: TvaRunningStatus::StartsShortly,
253 },
254 TvaIdEntry {
255 tva_id: 0xFFFF,
256 running_status: TvaRunningStatus::Reserved,
257 },
258 ],
259 };
260 let mut buf = vec![0u8; d.serialized_len()];
261 d.serialize_into(&mut buf).unwrap();
262 assert_eq!(TvaIdDescriptor::parse(&buf).unwrap(), d);
263 }
264
265 #[test]
266 fn serialize_rejects_running_status_over_range() {
267 let d = TvaIdDescriptor {
268 entries: vec![TvaIdEntry {
269 tva_id: 0,
270 running_status: TvaRunningStatus::Unallocated(0x08),
271 }],
272 };
273 let mut buf = vec![0u8; d.serialized_len()];
274 assert!(matches!(
275 d.serialize_into(&mut buf).unwrap_err(),
276 Error::InvalidDescriptor { .. }
277 ));
278 }
279
280 #[cfg(feature = "serde")]
281 #[test]
282 fn serde_round_trip() {
283 let d = TvaIdDescriptor {
284 entries: vec![TvaIdEntry {
285 tva_id: 0x4242,
286 running_status: TvaRunningStatus::Running,
287 }],
288 };
289 let j = serde_json::to_string(&d).unwrap();
290 let _v: serde_json::Value = serde_json::from_str(&j).unwrap();
292 }
293
294 #[test]
295 fn tva_running_status_full_range_round_trip() {
296 for b in 0..=0xFF_u8 {
297 let rs = TvaRunningStatus::from_u8(b);
298 assert_eq!(rs.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
299 }
300 }
301
302 #[test]
303 fn tva_running_status_name_for_known() {
304 assert_eq!(TvaRunningStatus::NotYetRunning.name(), "not yet running");
305 assert_eq!(TvaRunningStatus::Running.name(), "running");
306 assert_eq!(TvaRunningStatus::Completed.name(), "completed");
307 assert_eq!(TvaRunningStatus::Unallocated(0x07).name(), "unallocated");
308 }
309}