1use super::descriptor_body;
19use crate::error::{Error, Result};
20use crate::text::DvbText;
21use dvb_common::{Parse, Serialize};
22
23pub const TAG: u8 = 0x57;
25const HEADER_LEN: usize = 2;
26const FIXED_LEN: usize = 3;
28
29const FOREIGN_AVAIL_MASK: u8 = 0x20;
30const CONNECTION_TYPE_MASK: u8 = 0x1F;
31const BYTE0_RESERVED: u8 = 0xC0;
32const BYTE1_RESERVED: u8 = 0x80;
33const BYTE2_RESERVED: u8 = 0x80;
34
35const MAX_COUNTRY_PREFIX: usize = 0x03;
36const MAX_INTL_AREA: usize = 0x07;
37const MAX_OPERATOR: usize = 0x03;
38const MAX_NATIONAL_AREA: usize = 0x07;
39const MAX_CORE_NUMBER: usize = 0x0F;
40
41#[derive(Debug, Clone, PartialEq, Eq)]
43#[cfg_attr(feature = "serde", derive(serde::Serialize))]
44#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
45pub struct TelephoneDescriptor<'a> {
46 pub foreign_availability: bool,
48 pub connection_type: u8,
50 pub country_prefix: DvbText<'a>,
52 pub international_area_code: DvbText<'a>,
54 pub operator_code: DvbText<'a>,
56 pub national_area_code: DvbText<'a>,
58 pub core_number: DvbText<'a>,
60}
61
62impl<'a> Parse<'a> for TelephoneDescriptor<'a> {
63 type Error = crate::error::Error;
64 fn parse(bytes: &'a [u8]) -> Result<Self> {
65 let body = descriptor_body(
66 bytes,
67 TAG,
68 "TelephoneDescriptor",
69 "unexpected tag for telephone_descriptor",
70 )?;
71 if body.len() < FIXED_LEN {
72 return Err(Error::InvalidDescriptor {
73 tag: TAG,
74 reason: "telephone_descriptor length too short for fixed fields",
75 });
76 }
77 let foreign_availability = body[0] & FOREIGN_AVAIL_MASK != 0;
78 let connection_type = body[0] & CONNECTION_TYPE_MASK;
79 let country_prefix_length = ((body[1] >> 5) & 0x03) as usize;
80 let international_area_code_length = ((body[1] >> 2) & 0x07) as usize;
81 let operator_code_length = (body[1] & 0x03) as usize;
82 let national_area_code_length = ((body[2] >> 4) & 0x07) as usize;
83 let core_number_length = (body[2] & 0x0F) as usize;
84 let total_chars = country_prefix_length
85 + international_area_code_length
86 + operator_code_length
87 + national_area_code_length
88 + core_number_length;
89 if FIXED_LEN + total_chars > body.len() {
90 return Err(Error::InvalidDescriptor {
91 tag: TAG,
92 reason: "sum of telephone char-field lengths exceeds descriptor body",
93 });
94 }
95 let mut pos = FIXED_LEN;
96 let mut take = |n: usize| {
97 let s = &body[pos..pos + n];
98 pos += n;
99 s
100 };
101 let country_prefix = take(country_prefix_length);
102 let international_area_code = take(international_area_code_length);
103 let operator_code = take(operator_code_length);
104 let national_area_code = take(national_area_code_length);
105 let core_number = take(core_number_length);
106 Ok(Self {
107 foreign_availability,
108 connection_type,
109 country_prefix: DvbText::new(country_prefix),
110 international_area_code: DvbText::new(international_area_code),
111 operator_code: DvbText::new(operator_code),
112 national_area_code: DvbText::new(national_area_code),
113 core_number: DvbText::new(core_number),
114 })
115 }
116}
117
118impl Serialize for TelephoneDescriptor<'_> {
119 type Error = crate::error::Error;
120 fn serialized_len(&self) -> usize {
121 HEADER_LEN
122 + FIXED_LEN
123 + self.country_prefix.raw().len()
124 + self.international_area_code.raw().len()
125 + self.operator_code.raw().len()
126 + self.national_area_code.raw().len()
127 + self.core_number.raw().len()
128 }
129
130 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
131 let len = self.serialized_len();
132 if buf.len() < len {
133 return Err(Error::OutputBufferTooSmall {
134 need: len,
135 have: buf.len(),
136 });
137 }
138 if self.country_prefix.raw().len() > MAX_COUNTRY_PREFIX
139 || self.international_area_code.raw().len() > MAX_INTL_AREA
140 || self.operator_code.raw().len() > MAX_OPERATOR
141 || self.national_area_code.raw().len() > MAX_NATIONAL_AREA
142 || self.core_number.raw().len() > MAX_CORE_NUMBER
143 {
144 return Err(Error::InvalidDescriptor {
145 tag: TAG,
146 reason: "telephone char-field exceeds its length-field capacity",
147 });
148 }
149 buf[0] = TAG;
150 buf[1] = (len - HEADER_LEN) as u8;
151 buf[2] = BYTE0_RESERVED
152 | if self.foreign_availability {
153 FOREIGN_AVAIL_MASK
154 } else {
155 0
156 }
157 | (self.connection_type & CONNECTION_TYPE_MASK);
158 buf[3] = BYTE1_RESERVED
159 | ((self.country_prefix.raw().len() as u8 & 0x03) << 5)
160 | ((self.international_area_code.raw().len() as u8 & 0x07) << 2)
161 | (self.operator_code.raw().len() as u8 & 0x03);
162 buf[4] = BYTE2_RESERVED
163 | ((self.national_area_code.raw().len() as u8 & 0x07) << 4)
164 | (self.core_number.raw().len() as u8 & 0x0F);
165 let mut pos = HEADER_LEN + FIXED_LEN;
166 for field in [
167 self.country_prefix.raw(),
168 self.international_area_code.raw(),
169 self.operator_code.raw(),
170 self.national_area_code.raw(),
171 self.core_number.raw(),
172 ] {
173 buf[pos..pos + field.len()].copy_from_slice(field);
174 pos += field.len();
175 }
176 Ok(len)
177 }
178}
179impl<'a> crate::traits::DescriptorDef<'a> for TelephoneDescriptor<'a> {
180 const TAG: u8 = TAG;
181 const NAME: &'static str = "TELEPHONE";
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 fn sample() -> TelephoneDescriptor<'static> {
189 TelephoneDescriptor {
190 foreign_availability: true,
191 connection_type: 0x05,
192 country_prefix: DvbText::new(b"44"),
193 international_area_code: DvbText::new(b"171"),
194 operator_code: DvbText::new(b"01"),
195 national_area_code: DvbText::new(b"207"),
196 core_number: DvbText::new(b"123456"),
197 }
198 }
199
200 #[test]
201 fn parse_extracts_all_fields() {
202 let d = sample();
203 let mut buf = vec![0u8; d.serialized_len()];
204 d.serialize_into(&mut buf).unwrap();
205 let p = TelephoneDescriptor::parse(&buf).unwrap();
206 assert!(p.foreign_availability);
207 assert_eq!(p.connection_type, 0x05);
208 assert_eq!(p.country_prefix.raw(), b"44");
209 assert_eq!(p.international_area_code.raw(), b"171");
210 assert_eq!(p.operator_code.raw(), b"01");
211 assert_eq!(p.national_area_code.raw(), b"207");
212 assert_eq!(p.core_number.raw(), b"123456");
213 }
214
215 #[test]
216 fn parse_minimal_no_chars() {
217 let bytes = [TAG, 3, 0x20, 0x00, 0x00];
218 let d = TelephoneDescriptor::parse(&bytes).unwrap();
219 assert!(d.foreign_availability);
220 assert!(d.country_prefix.raw().is_empty());
221 assert!(d.core_number.raw().is_empty());
222 }
223
224 #[test]
225 fn parse_rejects_wrong_tag() {
226 assert!(matches!(
227 TelephoneDescriptor::parse(&[0x58, 3, 0, 0, 0]).unwrap_err(),
228 Error::InvalidDescriptor { tag: 0x58, .. }
229 ));
230 }
231
232 #[test]
233 fn parse_rejects_short_buffer() {
234 let bytes = [TAG, 3, 0x20, 0x00];
235 assert!(matches!(
236 TelephoneDescriptor::parse(&bytes).unwrap_err(),
237 Error::BufferTooShort { .. }
238 ));
239 }
240
241 #[test]
242 fn parse_rejects_length_below_fixed() {
243 let bytes = [TAG, 2, 0x20, 0x00];
244 assert!(matches!(
245 TelephoneDescriptor::parse(&bytes).unwrap_err(),
246 Error::InvalidDescriptor { tag: TAG, .. }
247 ));
248 }
249
250 #[test]
251 fn parse_rejects_char_lengths_overrun() {
252 let bytes = [TAG, 3, 0x20, 0x60, 0x00];
253 assert!(matches!(
254 TelephoneDescriptor::parse(&bytes).unwrap_err(),
255 Error::InvalidDescriptor { tag: TAG, .. }
256 ));
257 }
258
259 #[test]
260 fn serialize_round_trip() {
261 let d = sample();
262 let mut buf = vec![0u8; d.serialized_len()];
263 d.serialize_into(&mut buf).unwrap();
264 assert_eq!(TelephoneDescriptor::parse(&buf).unwrap(), d);
265 }
266
267 #[test]
268 fn serialize_emits_reserved_ones() {
269 let d = TelephoneDescriptor {
270 foreign_availability: false,
271 connection_type: 0,
272 country_prefix: DvbText::new(b""),
273 international_area_code: DvbText::new(b""),
274 operator_code: DvbText::new(b""),
275 national_area_code: DvbText::new(b""),
276 core_number: DvbText::new(b""),
277 };
278 let mut buf = vec![0u8; d.serialized_len()];
279 d.serialize_into(&mut buf).unwrap();
280 assert_eq!(buf[2] & BYTE0_RESERVED, BYTE0_RESERVED);
281 assert_eq!(buf[3] & BYTE1_RESERVED, BYTE1_RESERVED);
282 assert_eq!(buf[4] & BYTE2_RESERVED, BYTE2_RESERVED);
283 }
284
285 #[test]
286 fn serialize_rejects_over_range_field() {
287 let d = TelephoneDescriptor {
288 foreign_availability: false,
289 connection_type: 0,
290 country_prefix: DvbText::new(b""),
291 international_area_code: DvbText::new(b""),
292 operator_code: DvbText::new(b"1234"),
293 national_area_code: DvbText::new(b""),
294 core_number: DvbText::new(b""),
295 };
296 let mut buf = vec![0u8; d.serialized_len()];
297 assert!(matches!(
298 d.serialize_into(&mut buf).unwrap_err(),
299 Error::InvalidDescriptor { tag: TAG, .. }
300 ));
301 }
302
303 #[cfg(feature = "serde")]
304 #[test]
305 fn serde_serialize_stable() {
306 let d = sample();
307 let json = serde_json::to_string(&d).unwrap();
308 assert!(json.contains("connection_type"));
309 assert!(json.contains("foreign_availability"));
310 assert_eq!(json, serde_json::to_string(&sample()).unwrap());
311 }
312}