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