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