dvb_si/descriptors/
multilingual_bouquet_name.rs1use crate::error::{Error, Result};
7use crate::text::{DvbText, LangCode};
8use crate::traits::Descriptor;
9use dvb_common::{Parse, Serialize};
10
11pub const TAG: u8 = 0x5C;
13const HEADER_LEN: usize = 2;
14const LANG_LEN: usize = 3;
15const NAME_LEN_FIELD: usize = 1;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize))]
20pub struct BouquetNameEntry<'a> {
21 pub language_code: LangCode,
23 pub bouquet_name: DvbText<'a>,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
29#[cfg_attr(feature = "serde", derive(serde::Serialize))]
30pub struct MultilingualBouquetNameDescriptor<'a> {
31 pub entries: Vec<BouquetNameEntry<'a>>,
33}
34
35impl<'a> Parse<'a> for MultilingualBouquetNameDescriptor<'a> {
36 type Error = crate::error::Error;
37 fn parse(bytes: &'a [u8]) -> Result<Self> {
38 if bytes.len() < HEADER_LEN {
39 return Err(Error::BufferTooShort {
40 need: HEADER_LEN,
41 have: bytes.len(),
42 what: "MultilingualBouquetNameDescriptor header",
43 });
44 }
45 if bytes[0] != TAG {
46 return Err(Error::InvalidDescriptor {
47 tag: bytes[0],
48 reason: "unexpected tag for multilingual_bouquet_name_descriptor",
49 });
50 }
51 let length = bytes[1] as usize;
52 let end = HEADER_LEN + length;
53 if bytes.len() < end {
54 return Err(Error::BufferTooShort {
55 need: end,
56 have: bytes.len(),
57 what: "MultilingualBouquetNameDescriptor body",
58 });
59 }
60 let mut entries = Vec::new();
61 let mut pos = HEADER_LEN;
62 while pos < end {
63 if pos + LANG_LEN + NAME_LEN_FIELD > end {
64 return Err(Error::InvalidDescriptor {
65 tag: TAG,
66 reason: "entry header runs past descriptor end",
67 });
68 }
69 let language_code = LangCode([bytes[pos], bytes[pos + 1], bytes[pos + 2]]);
70 let name_len = bytes[pos + LANG_LEN] as usize;
71 let name_start = pos + LANG_LEN + NAME_LEN_FIELD;
72 let name_end = name_start + name_len;
73 if name_end > end {
74 return Err(Error::InvalidDescriptor {
75 tag: TAG,
76 reason: "name_length runs past descriptor end",
77 });
78 }
79 entries.push(BouquetNameEntry {
80 language_code,
81 bouquet_name: DvbText::new(&bytes[name_start..name_end]),
82 });
83 pos = name_end;
84 }
85 Ok(Self { entries })
86 }
87}
88
89impl Serialize for MultilingualBouquetNameDescriptor<'_> {
90 type Error = crate::error::Error;
91 fn serialized_len(&self) -> usize {
92 HEADER_LEN
93 + self
94 .entries
95 .iter()
96 .map(|e| LANG_LEN + NAME_LEN_FIELD + e.bouquet_name.len())
97 .sum::<usize>()
98 }
99
100 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
101 for e in &self.entries {
102 if e.bouquet_name.len() > u8::MAX as usize {
103 return Err(Error::InvalidDescriptor {
104 tag: TAG,
105 reason: "bouquet_name exceeds 255 bytes (name_length is 8-bit)",
106 });
107 }
108 }
109 let len = self.serialized_len();
110 let body = len - HEADER_LEN;
111 if body > u8::MAX as usize {
112 return Err(Error::InvalidDescriptor {
113 tag: TAG,
114 reason: "multilingual_bouquet_name_descriptor body exceeds 255 bytes",
115 });
116 }
117 if buf.len() < len {
118 return Err(Error::OutputBufferTooSmall {
119 need: len,
120 have: buf.len(),
121 });
122 }
123 buf[0] = TAG;
124 buf[1] = body as u8;
125 let mut pos = HEADER_LEN;
126 for e in &self.entries {
127 buf[pos..pos + LANG_LEN].copy_from_slice(&e.language_code.0);
128 buf[pos + LANG_LEN] = e.bouquet_name.len() as u8;
129 let name_start = pos + LANG_LEN + NAME_LEN_FIELD;
130 buf[name_start..name_start + e.bouquet_name.len()]
131 .copy_from_slice(e.bouquet_name.raw());
132 pos = name_start + e.bouquet_name.len();
133 }
134 Ok(len)
135 }
136}
137
138impl<'a> Descriptor<'a> for MultilingualBouquetNameDescriptor<'a> {
139 const TAG: u8 = TAG;
140 fn descriptor_length(&self) -> u8 {
141 (self.serialized_len() - HEADER_LEN) as u8
142 }
143}
144
145impl<'a> crate::traits::DescriptorDef<'a> for MultilingualBouquetNameDescriptor<'a> {
146 const TAG: u8 = TAG;
147 const NAME: &'static str = "MULTILINGUAL_BOUQUET_NAME";
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 fn build(entries: &[([u8; 3], &[u8])]) -> Vec<u8> {
155 let body: usize = entries.iter().map(|(_, n)| LANG_LEN + 1 + n.len()).sum();
156 let mut v = Vec::with_capacity(HEADER_LEN + body);
157 v.push(TAG);
158 v.push(body as u8);
159 for (lang, name) in entries {
160 v.extend_from_slice(lang);
161 v.push(name.len() as u8);
162 v.extend_from_slice(name);
163 }
164 v
165 }
166
167 #[test]
168 fn parse_single_entry() {
169 let bytes = build(&[(*b"eng", b"Sky")]);
170 let d = MultilingualBouquetNameDescriptor::parse(&bytes).unwrap();
171 assert_eq!(d.entries.len(), 1);
172 assert_eq!(d.entries[0].language_code, LangCode(*b"eng"));
173 assert_eq!(d.entries[0].bouquet_name.raw(), b"Sky");
174 }
175
176 #[test]
177 fn parse_multiple_entries() {
178 let bytes = build(&[(*b"eng", b"Pack"), (*b"fra", b"Bouquet")]);
179 let d = MultilingualBouquetNameDescriptor::parse(&bytes).unwrap();
180 assert_eq!(d.entries.len(), 2);
181 assert_eq!(d.entries[1].bouquet_name.raw(), b"Bouquet");
182 }
183
184 #[test]
185 fn parse_rejects_wrong_tag() {
186 let err = MultilingualBouquetNameDescriptor::parse(&[0x5B, 0]).unwrap_err();
187 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x5B, .. }));
188 }
189
190 #[test]
191 fn parse_rejects_short_buffer() {
192 let err = MultilingualBouquetNameDescriptor::parse(&[TAG]).unwrap_err();
193 assert!(matches!(err, Error::BufferTooShort { .. }));
194 }
195
196 #[test]
197 fn parse_rejects_name_length_overrun() {
198 let bytes = [TAG, 5, b'e', b'n', b'g', 100, 0];
199 let err = MultilingualBouquetNameDescriptor::parse(&bytes).unwrap_err();
200 assert!(matches!(err, Error::InvalidDescriptor { .. }));
201 }
202
203 #[test]
204 fn parse_rejects_truncated_entry_header() {
205 let bytes = [TAG, 2, b'e', b'n'];
206 let err = MultilingualBouquetNameDescriptor::parse(&bytes).unwrap_err();
207 assert!(matches!(err, Error::InvalidDescriptor { .. }));
208 }
209
210 #[test]
211 fn empty_descriptor_valid() {
212 let d = MultilingualBouquetNameDescriptor::parse(&[TAG, 0]).unwrap();
213 assert_eq!(d.entries.len(), 0);
214 }
215
216 #[test]
217 fn serialize_round_trip() {
218 let bytes = build(&[(*b"eng", b"Bouquet"), (*b"deu", b"Paket")]);
219 let parsed = MultilingualBouquetNameDescriptor::parse(&bytes).unwrap();
220 let mut buf = vec![0u8; parsed.serialized_len()];
221 parsed.serialize_into(&mut buf).unwrap();
222 assert_eq!(buf, bytes);
223 let re = MultilingualBouquetNameDescriptor::parse(&buf).unwrap();
224 assert_eq!(parsed, re);
225 }
226
227 #[test]
228 fn serialize_rejects_too_small_buffer() {
229 let d = MultilingualBouquetNameDescriptor {
230 entries: vec![BouquetNameEntry {
231 language_code: LangCode(*b"eng"),
232 bouquet_name: DvbText::new(b"X"),
233 }],
234 };
235 let mut tiny = [0u8; 3];
236 let err = d.serialize_into(&mut tiny).unwrap_err();
237 assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
238 }
239
240 #[test]
241 fn serialize_rejects_over_range_name() {
242 let name = vec![0u8; 256];
243 let d = MultilingualBouquetNameDescriptor {
244 entries: vec![BouquetNameEntry {
245 language_code: LangCode(*b"eng"),
246 bouquet_name: DvbText::new(&name),
247 }],
248 };
249 let mut buf = vec![0u8; d.serialized_len()];
250 let err = d.serialize_into(&mut buf).unwrap_err();
251 assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
252 }
253
254 #[cfg(feature = "serde")]
255 #[test]
256 fn serde_serialize_is_stable() {
257 let d = MultilingualBouquetNameDescriptor {
261 entries: vec![BouquetNameEntry {
262 language_code: LangCode(*b"eng"),
263 bouquet_name: DvbText::new(b"Sky"),
264 }],
265 };
266 let json = serde_json::to_string(&d).unwrap();
267 assert_eq!(json, serde_json::to_string(&d.clone()).unwrap());
268 }
269}