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 val = u16::from_be_bytes([sel[pos], sel[pos + 1]]);
99 pos += 2;
100 Some(val)
101 } else {
102 None
103 };
104 regions.push(TargetRegionNameEntry {
105 region_depth,
106 region_name,
107 primary_region_code,
108 secondary_region_code,
109 tertiary_region_code,
110 });
111 }
112 Ok(TargetRegionName {
113 country_code,
114 iso_639_language_code,
115 regions,
116 })
117 }
118}
119
120impl Serialize for TargetRegionName<'_> {
121 type Error = crate::error::Error;
122 fn serialized_len(&self) -> usize {
123 2 * ISO_639_LEN
124 + self
125 .regions
126 .iter()
127 .map(|r| {
128 1 + r.region_name.raw().len()
129 + 1
130 + if r.region_depth >= 2 { 1 } else { 0 }
131 + if r.region_depth == 3 { 2 } else { 0 }
132 })
133 .sum::<usize>()
134 }
135 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
136 let len = self.serialized_len();
137 if buf.len() < len {
138 return Err(Error::OutputBufferTooSmall {
139 need: len,
140 have: buf.len(),
141 });
142 }
143 buf[..ISO_639_LEN].copy_from_slice(&self.country_code.0);
144 buf[ISO_639_LEN..2 * ISO_639_LEN].copy_from_slice(&self.iso_639_language_code.0);
145 let mut pos = 2 * ISO_639_LEN;
146 for region in &self.regions {
147 let raw = region.region_name.raw();
148 buf[pos] = ((region.region_depth & 0x03) << 6) | (raw.len() as u8 & 0x3F);
149 pos += 1;
150 buf[pos..pos + raw.len()].copy_from_slice(raw);
151 pos += raw.len();
152 buf[pos] = region.primary_region_code;
153 pos += 1;
154 if let Some(sec) = region.secondary_region_code {
155 buf[pos] = sec;
156 pos += 1;
157 }
158 if let Some(tert) = region.tertiary_region_code {
159 buf[pos..pos + 2].copy_from_slice(&tert.to_be_bytes());
160 pos += 2;
161 }
162 }
163 Ok(len)
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use crate::descriptors::extension::test_support::*;
171 use crate::descriptors::extension::{ExtensionBody, ExtensionDescriptor};
172 use crate::text::LangCode;
173
174 #[test]
175 fn parse_target_region_name_structured() {
176 let sel = [b'g', b'b', b'r', b'e', b'n', b'g', 0x42, b'H', b'i', 0x12];
177 let bytes = wrap(0x0A, &sel);
178 let d = ExtensionDescriptor::parse(&bytes).unwrap();
179 match &d.body {
180 ExtensionBody::TargetRegionName(b) => {
181 assert_eq!(b.country_code, LangCode(*b"gbr"));
182 assert_eq!(b.iso_639_language_code, LangCode(*b"eng"));
183 assert_eq!(b.regions.len(), 1);
184 assert_eq!(b.regions[0].region_depth, 1);
185 assert_eq!(b.regions[0].region_name.raw(), b"Hi");
186 assert_eq!(b.regions[0].primary_region_code, 0x12);
187 assert_eq!(b.regions[0].secondary_region_code, None);
188 assert_eq!(b.regions[0].tertiary_region_code, None);
189 }
190 other => panic!("expected TargetRegionName, got {other:?}"),
191 }
192 round_trip(&d);
193 }
194
195 #[test]
196 fn target_region_name_tsduck_empty() {
197 let bytes = from_hex("7f070a666f6f626172");
198 let d = ExtensionDescriptor::parse(&bytes).unwrap();
199 match &d.body {
200 ExtensionBody::TargetRegionName(b) => {
201 assert_eq!(b.country_code, LangCode(*b"foo"));
202 assert_eq!(b.iso_639_language_code, LangCode(*b"bar"));
203 assert!(b.regions.is_empty());
204 }
205 other => panic!("expected TargetRegionName, got {other:?}"),
206 }
207 let mut buf = vec![0u8; d.serialized_len()];
208 d.serialize_into(&mut buf).unwrap();
209 assert_eq!(buf, bytes);
210 }
211
212 #[test]
213 fn target_region_name_tsduck_full() {
214 let bytes = from_hex(
215 "7f260a4142434445464c726567696f6e20666f6f203112cc726567696f6e2062617220323456789a",
216 );
217 let d = ExtensionDescriptor::parse(&bytes).unwrap();
218 match &d.body {
219 ExtensionBody::TargetRegionName(b) => {
220 assert_eq!(b.country_code, LangCode(*b"ABC"));
221 assert_eq!(b.iso_639_language_code, LangCode(*b"DEF"));
222 assert_eq!(b.regions.len(), 2);
223 assert_eq!(b.regions[0].region_depth, 1);
225 assert_eq!(b.regions[0].region_name.raw(), b"region foo 1");
226 assert_eq!(b.regions[0].primary_region_code, 0x12);
227 assert_eq!(b.regions[0].secondary_region_code, None);
228 assert_eq!(b.regions[0].tertiary_region_code, None);
229 assert_eq!(b.regions[1].region_depth, 3);
231 assert_eq!(b.regions[1].region_name.raw(), b"region bar 2");
232 assert_eq!(b.regions[1].primary_region_code, 0x34);
233 assert_eq!(b.regions[1].secondary_region_code, Some(0x56));
234 assert_eq!(b.regions[1].tertiary_region_code, Some(0x789A));
235 }
236 other => panic!("expected TargetRegionName, got {other:?}"),
237 }
238 let mut buf = vec![0u8; d.serialized_len()];
239 d.serialize_into(&mut buf).unwrap();
240 assert_eq!(buf, bytes);
241 }
242}