1use super::descriptor_body;
9use super::teletext::TeletextType;
10use crate::error::{Error, Result};
11use crate::text::LangCode;
12use alloc::vec::Vec;
13use dvb_common::{Parse, Serialize};
14
15pub const TAG: u8 = 0x46;
17const HEADER_LEN: usize = 2;
18const ENTRY_LEN: usize = 5;
19const LANG_LEN: usize = 3;
20const MAX_BODY_LEN: usize = u8::MAX as usize;
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25#[cfg_attr(feature = "serde", derive(serde::Serialize))]
26pub struct VbiTeletextEntry {
27 pub language_code: LangCode,
29 pub teletext_type: TeletextType,
31 pub magazine_number: u8,
33 pub page_number: u8,
35}
36
37#[derive(Debug, Clone, PartialEq, Eq)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize))]
40pub struct VbiTeletextDescriptor {
41 pub entries: Vec<VbiTeletextEntry>,
43}
44
45impl<'a> Parse<'a> for VbiTeletextDescriptor {
46 type Error = crate::error::Error;
47 fn parse(bytes: &'a [u8]) -> Result<Self> {
48 let body = descriptor_body(
49 bytes,
50 TAG,
51 "VbiTeletextDescriptor",
52 "unexpected tag for VBI_teletext_descriptor",
53 )?;
54 if body.len() % ENTRY_LEN != 0 {
55 return Err(Error::InvalidDescriptor {
56 tag: TAG,
57 reason: "descriptor_length must be a multiple of 5",
58 });
59 }
60 let mut entries = Vec::with_capacity(body.len() / ENTRY_LEN);
61 for chunk in body.chunks_exact(ENTRY_LEN) {
62 let language_code = LangCode([chunk[0], chunk[1], chunk[2]]);
63 let type_and_mag = chunk[LANG_LEN];
64 entries.push(VbiTeletextEntry {
65 language_code,
66 teletext_type: TeletextType::from_u8((type_and_mag >> 3) & 0x1F),
67 magazine_number: type_and_mag & 0x07,
68 page_number: chunk[LANG_LEN + 1],
69 });
70 }
71 Ok(Self { entries })
72 }
73}
74
75impl Serialize for VbiTeletextDescriptor {
76 type Error = crate::error::Error;
77 fn serialized_len(&self) -> usize {
78 HEADER_LEN + ENTRY_LEN * self.entries.len()
79 }
80
81 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
82 let body_len = ENTRY_LEN * self.entries.len();
83 if body_len > MAX_BODY_LEN {
85 return Err(Error::InvalidDescriptor {
86 tag: TAG,
87 reason: "VBI_teletext_descriptor body exceeds 255 bytes",
88 });
89 }
90 let len = self.serialized_len();
91 if buf.len() < len {
92 return Err(Error::OutputBufferTooSmall {
93 need: len,
94 have: buf.len(),
95 });
96 }
97 buf[0] = TAG;
98 buf[1] = body_len as u8;
99 let mut pos = HEADER_LEN;
100 for e in &self.entries {
101 buf[pos..pos + LANG_LEN].copy_from_slice(&e.language_code.0);
102 buf[pos + LANG_LEN] =
103 ((e.teletext_type.to_u8() & 0x1F) << 3) | (e.magazine_number & 0x07);
104 buf[pos + LANG_LEN + 1] = e.page_number;
105 pos += ENTRY_LEN;
106 }
107 Ok(len)
108 }
109}
110impl<'a> crate::traits::DescriptorDef<'a> for VbiTeletextDescriptor {
111 const TAG: u8 = TAG;
112 const NAME: &'static str = "VBI_TELETEXT";
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn parse_single_entry() {
121 let bytes = [TAG, 5, b'e', b'n', b'g', (1 << 3) | 2, 0x10];
122 let d = VbiTeletextDescriptor::parse(&bytes).unwrap();
123 assert_eq!(d.entries.len(), 1);
124 assert_eq!(d.entries[0].language_code, LangCode(*b"eng"));
125 assert_eq!(d.entries[0].teletext_type, TeletextType::InitialPage);
126 assert_eq!(d.entries[0].magazine_number, 2);
127 assert_eq!(d.entries[0].page_number, 0x10);
128 }
129
130 #[test]
131 fn parse_multiple_entries() {
132 let bytes = [
133 TAG,
134 10,
135 b'e',
136 b'n',
137 b'g',
138 (1 << 3) | 1,
139 0x10,
140 b'f',
141 b'r',
142 b'a',
143 (2 << 3) | 1,
144 0x20,
145 ];
146 let d = VbiTeletextDescriptor::parse(&bytes).unwrap();
147 assert_eq!(d.entries.len(), 2);
148 assert_eq!(d.entries[1].teletext_type, TeletextType::SubtitlePage);
149 assert_eq!(d.entries[1].language_code, LangCode(*b"fra"));
150 }
151
152 #[test]
153 fn parse_rejects_wrong_tag() {
154 assert!(matches!(
155 VbiTeletextDescriptor::parse(&[0x47, 0]).unwrap_err(),
156 Error::InvalidDescriptor { tag: 0x47, .. }
157 ));
158 }
159
160 #[test]
161 fn parse_rejects_short_buffer() {
162 let bytes = [TAG, 5, b'e', b'n'];
163 assert!(matches!(
164 VbiTeletextDescriptor::parse(&bytes).unwrap_err(),
165 Error::BufferTooShort { .. }
166 ));
167 }
168
169 #[test]
170 fn parse_rejects_length_not_multiple_of_5() {
171 let bytes = [TAG, 4, 0, 0, 0, 0];
172 assert!(matches!(
173 VbiTeletextDescriptor::parse(&bytes).unwrap_err(),
174 Error::InvalidDescriptor { tag: TAG, .. }
175 ));
176 }
177
178 #[test]
179 fn empty_descriptor_valid() {
180 let d = VbiTeletextDescriptor::parse(&[TAG, 0]).unwrap();
181 assert!(d.entries.is_empty());
182 }
183
184 #[test]
185 fn serialize_round_trip() {
186 let d = VbiTeletextDescriptor {
187 entries: vec![VbiTeletextEntry {
188 language_code: LangCode(*b"fra"),
189 teletext_type: TeletextType::SubtitlePage,
190 magazine_number: 8 & 0x07,
191 page_number: 0x88,
192 }],
193 };
194 let mut buf = vec![0u8; d.serialized_len()];
195 d.serialize_into(&mut buf).unwrap();
196 assert_eq!(VbiTeletextDescriptor::parse(&buf).unwrap(), d);
197 }
198
199 #[test]
200 fn serialize_rejects_over_range_body() {
201 let d = VbiTeletextDescriptor {
203 entries: vec![
204 VbiTeletextEntry {
205 language_code: LangCode(*b"eng"),
206 teletext_type: TeletextType::Reserved(1),
207 magazine_number: 1,
208 page_number: 0,
209 };
210 52
211 ],
212 };
213 let mut buf = vec![0u8; d.serialized_len()];
214 assert!(matches!(
215 d.serialize_into(&mut buf).unwrap_err(),
216 Error::InvalidDescriptor { tag: TAG, .. }
217 ));
218 }
219
220 #[cfg(feature = "serde")]
221 #[test]
222 fn serde_round_trip() {
223 let d = VbiTeletextDescriptor {
224 entries: vec![VbiTeletextEntry {
225 language_code: LangCode(*b"eng"),
226 teletext_type: TeletextType::SubtitlePage,
227 magazine_number: 1,
228 page_number: 0x10,
229 }],
230 };
231 let json = serde_json::to_string(&d).unwrap();
232 let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
234 }
235}