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