Skip to main content

dvb_si/descriptors/
multilingual_network_name.rs

1//! Multilingual Network Name Descriptor — ETSI EN 300 468 §6.2.24 (tag 0x5B).
2//!
3//! Table 78 (PDF p. 94). Carried in the NIT. A loop of (ISO 639-2 language
4//! code, network name) pairs, each name length-prefixed by an 8-bit field.
5
6use super::descriptor_body;
7use crate::error::{Error, Result};
8use crate::text::{DvbText, LangCode};
9use alloc::vec::Vec;
10use dvb_common::{Parse, Serialize};
11
12/// Descriptor tag for multilingual_network_name_descriptor.
13pub const TAG: u8 = 0x5B;
14const HEADER_LEN: usize = 2;
15const LANG_LEN: usize = 3;
16const NAME_LEN_FIELD: usize = 1;
17
18/// One localised network name.
19#[derive(Debug, Clone, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize))]
21#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
22pub struct NetworkNameEntry<'a> {
23    /// ISO 639-2 language code.
24    pub language_code: LangCode,
25    /// DVB Annex-A encoded network name.
26    pub network_name: DvbText<'a>,
27}
28
29/// Multilingual Network Name Descriptor (tag 0x5B).
30#[derive(Debug, Clone, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize))]
32#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
33pub struct MultilingualNetworkNameDescriptor<'a> {
34    /// Localised names in wire order.
35    pub entries: Vec<NetworkNameEntry<'a>>,
36}
37
38impl<'a> Parse<'a> for MultilingualNetworkNameDescriptor<'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            "MultilingualNetworkNameDescriptor",
45            "unexpected tag for multilingual_network_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(NetworkNameEntry {
67                language_code,
68                network_name: DvbText::new(&body[name_start..name_end]),
69            });
70            pos = name_end;
71        }
72        Ok(Self { entries })
73    }
74}
75
76impl Serialize for MultilingualNetworkNameDescriptor<'_> {
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.network_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.network_name.len() > u8::MAX as usize {
90                return Err(Error::InvalidDescriptor {
91                    tag: TAG,
92                    reason: "network_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_network_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.network_name.len() as u8;
116            let name_start = pos + LANG_LEN + NAME_LEN_FIELD;
117            buf[name_start..name_start + e.network_name.len()]
118                .copy_from_slice(e.network_name.raw());
119            pos = name_start + e.network_name.len();
120        }
121        Ok(len)
122    }
123}
124impl<'a> crate::traits::DescriptorDef<'a> for MultilingualNetworkNameDescriptor<'a> {
125    const TAG: u8 = TAG;
126    const NAME: &'static str = "MULTILINGUAL_NETWORK_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"BBC")]);
149        let d = MultilingualNetworkNameDescriptor::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].network_name.raw(), b"BBC");
153    }
154
155    #[test]
156    fn parse_multiple_entries() {
157        let bytes = build(&[(*b"eng", b"Net"), (*b"fra", b"Reseau")]);
158        let d = MultilingualNetworkNameDescriptor::parse(&bytes).unwrap();
159        assert_eq!(d.entries.len(), 2);
160        assert_eq!(d.entries[1].network_name.raw(), b"Reseau");
161    }
162
163    #[test]
164    fn parse_rejects_wrong_tag() {
165        let err = MultilingualNetworkNameDescriptor::parse(&[0x5C, 0]).unwrap_err();
166        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x5C, .. }));
167    }
168
169    #[test]
170    fn parse_rejects_short_buffer() {
171        let err = MultilingualNetworkNameDescriptor::parse(&[TAG]).unwrap_err();
172        assert!(matches!(err, Error::BufferTooShort { .. }));
173    }
174
175    #[test]
176    fn parse_rejects_name_length_overrun() {
177        // name_len=100 but body only carries a few bytes.
178        let bytes = [TAG, 5, b'e', b'n', b'g', 100, 0];
179        let err = MultilingualNetworkNameDescriptor::parse(&bytes).unwrap_err();
180        assert!(matches!(err, Error::InvalidDescriptor { .. }));
181    }
182
183    #[test]
184    fn parse_rejects_truncated_entry_header() {
185        // Only 2 bytes of a language code present in the body.
186        let bytes = [TAG, 2, b'e', b'n'];
187        let err = MultilingualNetworkNameDescriptor::parse(&bytes).unwrap_err();
188        assert!(matches!(err, Error::InvalidDescriptor { .. }));
189    }
190
191    #[test]
192    fn empty_descriptor_valid() {
193        let d = MultilingualNetworkNameDescriptor::parse(&[TAG, 0]).unwrap();
194        assert_eq!(d.entries.len(), 0);
195    }
196
197    #[test]
198    fn serialize_round_trip() {
199        let bytes = build(&[(*b"eng", b"Network"), (*b"deu", b"Netz")]);
200        let parsed = MultilingualNetworkNameDescriptor::parse(&bytes).unwrap();
201        let mut buf = vec![0u8; parsed.serialized_len()];
202        parsed.serialize_into(&mut buf).unwrap();
203        assert_eq!(buf, bytes);
204        let re = MultilingualNetworkNameDescriptor::parse(&buf).unwrap();
205        assert_eq!(parsed, re);
206    }
207
208    #[test]
209    fn serialize_rejects_too_small_buffer() {
210        let d = MultilingualNetworkNameDescriptor {
211            entries: vec![NetworkNameEntry {
212                language_code: LangCode(*b"eng"),
213                network_name: DvbText::new(b"X"),
214            }],
215        };
216        let mut tiny = [0u8; 3];
217        let err = d.serialize_into(&mut tiny).unwrap_err();
218        assert!(matches!(err, Error::OutputBufferTooSmall { .. }));
219    }
220
221    #[test]
222    fn serialize_rejects_over_range_name() {
223        let name = vec![0u8; 256];
224        let d = MultilingualNetworkNameDescriptor {
225            entries: vec![NetworkNameEntry {
226                language_code: LangCode(*b"eng"),
227                network_name: DvbText::new(&name),
228            }],
229        };
230        let mut buf = vec![0u8; d.serialized_len()];
231        let err = d.serialize_into(&mut buf).unwrap_err();
232        assert!(matches!(err, Error::InvalidDescriptor { tag: TAG, .. }));
233    }
234
235    #[cfg(feature = "serde")]
236    #[test]
237    fn serde_serialize_is_stable() {
238        let d = MultilingualNetworkNameDescriptor {
239            entries: vec![NetworkNameEntry {
240                language_code: LangCode(*b"eng"),
241                network_name: DvbText::new(b"BBC"),
242            }],
243        };
244        let json = serde_json::to_string(&d).unwrap();
245        assert!(json.contains("\"language_code\""));
246        assert!(json.contains("\"eng\""));
247        assert!(json.contains("\"BBC\""));
248    }
249}