dvb_si/descriptors/extension/
target_region_name.rs1use super::*;
3use alloc::vec::Vec;
4
5impl<'a> ExtensionBodyDef<'a> for TargetRegionName<'a> {
6 const TAG_EXTENSION: u8 = 0x0A;
7 const NAME: &'static str = "TARGET_REGION_NAME";
8}
9#[derive(Debug, Clone, PartialEq, Eq)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize))]
12#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
13pub struct TargetRegionName<'a> {
14 pub country_code: LangCode,
16 pub iso_639_language_code: LangCode,
18 pub regions: Vec<TargetRegionNameEntry<'a>>,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize))]
25#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
26pub struct TargetRegionNameEntry<'a> {
27 pub region_depth: u8,
29 pub region_name: DvbText<'a>,
31 pub primary_region_code: u8,
33 pub secondary_region_code: Option<u8>,
35 pub tertiary_region_code: Option<u16>,
37}
38
39impl<'a> Parse<'a> for TargetRegionName<'a> {
40 type Error = crate::error::Error;
41 fn parse(sel: &'a [u8]) -> Result<Self> {
42 if sel.len() < 2 * ISO_639_LEN {
43 return Err(Error::BufferTooShort {
44 need: 2 * ISO_639_LEN,
45 have: sel.len(),
46 what: "target_region_name body",
47 });
48 }
49 let country_code = LangCode([sel[0], sel[1], sel[2]]);
50 let iso_639_language_code = LangCode([sel[3], sel[4], sel[5]]);
51 let mut regions = Vec::new();
52 let mut pos = 2 * ISO_639_LEN;
53 while pos < sel.len() {
54 let byte = sel[pos];
55 pos += 1;
56 let region_depth = byte >> 6;
57 let name_length = (byte & 0x3F) as usize;
58 if pos + name_length > sel.len() {
59 return Err(Error::BufferTooShort {
60 need: pos + name_length,
61 have: sel.len(),
62 what: "target_region_name body",
63 });
64 }
65 let region_name = DvbText::new(&sel[pos..pos + name_length]);
66 pos += name_length;
67 if pos >= sel.len() {
68 return Err(Error::BufferTooShort {
69 need: pos + 1,
70 have: sel.len(),
71 what: "target_region_name body",
72 });
73 }
74 let primary_region_code = sel[pos];
75 pos += 1;
76 let secondary_region_code = if region_depth >= 2 {
77 if pos >= sel.len() {
78 return Err(Error::BufferTooShort {
79 need: pos + 1,
80 have: sel.len(),
81 what: "target_region_name body",
82 });
83 }
84 let val = sel[pos];
85 pos += 1;
86 Some(val)
87 } else {
88 None
89 };
90 let tertiary_region_code = if region_depth == 3 {
91 if pos + 1 >= sel.len() {
92 return Err(Error::BufferTooShort {
93 need: pos + 2,
94 have: sel.len(),
95 what: "target_region_name body",
96 });
97 }
98 let (b, _) = sel
99 .get(pos..)
100 .and_then(|s| s.split_first_chunk::<2>())
101 .ok_or(Error::BufferTooShort {
102 need: pos + 2,
103 have: sel.len(),
104 what: "target_region_name body",
105 })?;
106 let val = u16::from_be_bytes(*b);
107 pos += 2;
108 Some(val)
109 } else {
110 None
111 };
112 regions.push(TargetRegionNameEntry {
113 region_depth,
114 region_name,
115 primary_region_code,
116 secondary_region_code,
117 tertiary_region_code,
118 });
119 }
120 Ok(TargetRegionName {
121 country_code,
122 iso_639_language_code,
123 regions,
124 })
125 }
126}
127
128impl Serialize for TargetRegionName<'_> {
129 type Error = crate::error::Error;
130 fn serialized_len(&self) -> usize {
131 2 * ISO_639_LEN
132 + self
133 .regions
134 .iter()
135 .map(|r| {
136 1 + r.region_name.raw().len()
137 + 1
138 + if r.region_depth >= 2 { 1 } else { 0 }
139 + if r.region_depth == 3 { 2 } else { 0 }
140 })
141 .sum::<usize>()
142 }
143 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
144 let len = self.serialized_len();
145 if buf.len() < len {
146 return Err(Error::OutputBufferTooSmall {
147 need: len,
148 have: buf.len(),
149 });
150 }
151 buf[..ISO_639_LEN].copy_from_slice(&self.country_code.0);
152 buf[ISO_639_LEN..2 * ISO_639_LEN].copy_from_slice(&self.iso_639_language_code.0);
153 let mut pos = 2 * ISO_639_LEN;
154 for region in &self.regions {
155 let raw = region.region_name.raw();
156 buf[pos] = ((region.region_depth & 0x03) << 6) | (raw.len() as u8 & 0x3F);
157 pos += 1;
158 buf[pos..pos + raw.len()].copy_from_slice(raw);
159 pos += raw.len();
160 buf[pos] = region.primary_region_code;
161 pos += 1;
162 if let Some(sec) = region.secondary_region_code {
163 buf[pos] = sec;
164 pos += 1;
165 }
166 if let Some(tert) = region.tertiary_region_code {
167 buf[pos..pos + 2].copy_from_slice(&tert.to_be_bytes());
168 pos += 2;
169 }
170 }
171 Ok(len)
172 }
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use crate::descriptors::extension::test_support::*;
179 use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor};
180 use crate::text::LangCode;
181
182 #[test]
183 fn parse_target_region_name_structured() {
184 let sel = [b'g', b'b', b'r', b'e', b'n', b'g', 0x42, b'H', b'i', 0x12];
185 let bytes = wrap(0x0A, &sel);
186 let d = ExtensionDescriptor::parse(&bytes).unwrap();
187 match &d.body {
188 ExtensionBody::TargetRegionName(b) => {
189 assert_eq!(b.country_code, LangCode(*b"gbr"));
190 assert_eq!(b.iso_639_language_code, LangCode(*b"eng"));
191 assert_eq!(b.regions.len(), 1);
192 assert_eq!(b.regions[0].region_depth, 1);
193 assert_eq!(b.regions[0].region_name.raw(), b"Hi");
194 assert_eq!(b.regions[0].primary_region_code, 0x12);
195 assert_eq!(b.regions[0].secondary_region_code, None);
196 assert_eq!(b.regions[0].tertiary_region_code, None);
197 }
198 other => panic!("expected TargetRegionName, got {other:?}"),
199 }
200 round_trip(&d);
201 }
202
203 #[test]
204 fn target_region_name_tsduck_empty() {
205 let bytes = from_hex("7f070a666f6f626172");
206 let d = ExtensionDescriptor::parse(&bytes).unwrap();
207 match &d.body {
208 ExtensionBody::TargetRegionName(b) => {
209 assert_eq!(b.country_code, LangCode(*b"foo"));
210 assert_eq!(b.iso_639_language_code, LangCode(*b"bar"));
211 assert!(b.regions.is_empty());
212 }
213 other => panic!("expected TargetRegionName, got {other:?}"),
214 }
215 let mut buf = vec![0u8; d.serialized_len()];
216 d.serialize_into(&mut buf).unwrap();
217 assert_eq!(buf, bytes);
218 }
219
220 #[test]
221 fn target_region_name_tsduck_full() {
222 let bytes = from_hex(
223 "7f260a4142434445464c726567696f6e20666f6f203112cc726567696f6e2062617220323456789a",
224 );
225 let d = ExtensionDescriptor::parse(&bytes).unwrap();
226 match &d.body {
227 ExtensionBody::TargetRegionName(b) => {
228 assert_eq!(b.country_code, LangCode(*b"ABC"));
229 assert_eq!(b.iso_639_language_code, LangCode(*b"DEF"));
230 assert_eq!(b.regions.len(), 2);
231 assert_eq!(b.regions[0].region_depth, 1);
233 assert_eq!(b.regions[0].region_name.raw(), b"region foo 1");
234 assert_eq!(b.regions[0].primary_region_code, 0x12);
235 assert_eq!(b.regions[0].secondary_region_code, None);
236 assert_eq!(b.regions[0].tertiary_region_code, None);
237 assert_eq!(b.regions[1].region_depth, 3);
239 assert_eq!(b.regions[1].region_name.raw(), b"region bar 2");
240 assert_eq!(b.regions[1].primary_region_code, 0x34);
241 assert_eq!(b.regions[1].secondary_region_code, Some(0x56));
242 assert_eq!(b.regions[1].tertiary_region_code, Some(0x789A));
243 }
244 other => panic!("expected TargetRegionName, got {other:?}"),
245 }
246 let mut buf = vec![0u8; d.serialized_len()];
247 d.serialize_into(&mut buf).unwrap();
248 assert_eq!(buf, bytes);
249 }
250}