1use super::descriptor_body;
18use crate::error::{Error, Result};
19use dvb_common::{Parse, Serialize};
20
21pub const TAG: u8 = 0x57;
23const HEADER_LEN: usize = 2;
24const FIXED_LEN: usize = 3;
26
27const FOREIGN_AVAIL_MASK: u8 = 0x20; const CONNECTION_TYPE_MASK: u8 = 0x1F; const BYTE0_RESERVED: u8 = 0xC0; const BYTE1_RESERVED: u8 = 0x80; const BYTE2_RESERVED: u8 = 0x80; const MAX_COUNTRY_PREFIX: usize = 0x03; const MAX_INTL_AREA: usize = 0x07; const MAX_OPERATOR: usize = 0x03; const MAX_NATIONAL_AREA: usize = 0x07; const MAX_CORE_NUMBER: usize = 0x0F; #[derive(Debug, Clone, PartialEq, Eq)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize))]
43#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
44pub struct TelephoneDescriptor<'a> {
45 pub foreign_availability: bool,
47 pub connection_type: u8,
49 pub country_prefix: &'a [u8],
51 pub international_area_code: &'a [u8],
53 pub operator_code: &'a [u8],
55 pub national_area_code: &'a [u8],
57 pub core_number: &'a [u8],
59}
60
61impl<'a> Parse<'a> for TelephoneDescriptor<'a> {
62 type Error = crate::error::Error;
63 fn parse(bytes: &'a [u8]) -> Result<Self> {
64 let body = descriptor_body(
65 bytes,
66 TAG,
67 "TelephoneDescriptor",
68 "unexpected tag for telephone_descriptor",
69 )?;
70 if body.len() < FIXED_LEN {
71 return Err(Error::InvalidDescriptor {
72 tag: TAG,
73 reason: "telephone_descriptor length too short for fixed fields",
74 });
75 }
76 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,
110 international_area_code,
111 operator_code,
112 national_area_code,
113 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.len()
124 + self.international_area_code.len()
125 + self.operator_code.len()
126 + self.national_area_code.len()
127 + self.core_number.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.len() > MAX_COUNTRY_PREFIX
140 || self.international_area_code.len() > MAX_INTL_AREA
141 || self.operator_code.len() > MAX_OPERATOR
142 || self.national_area_code.len() > MAX_NATIONAL_AREA
143 || self.core_number.len() > MAX_CORE_NUMBER
144 {
145 return Err(Error::InvalidDescriptor {
146 tag: TAG,
147 reason: "telephone char-field exceeds its length-field capacity",
148 });
149 }
150 buf[0] = TAG;
151 buf[1] = (len - HEADER_LEN) as u8;
152 buf[2] = BYTE0_RESERVED
154 | if self.foreign_availability {
155 FOREIGN_AVAIL_MASK
156 } else {
157 0
158 }
159 | (self.connection_type & CONNECTION_TYPE_MASK);
160 buf[3] = BYTE1_RESERVED
162 | ((self.country_prefix.len() as u8 & 0x03) << 5)
163 | ((self.international_area_code.len() as u8 & 0x07) << 2)
164 | (self.operator_code.len() as u8 & 0x03);
165 buf[4] = BYTE2_RESERVED
167 | ((self.national_area_code.len() as u8 & 0x07) << 4)
168 | (self.core_number.len() as u8 & 0x0F);
169 let mut pos = HEADER_LEN + FIXED_LEN;
170 for field in [
171 self.country_prefix,
172 self.international_area_code,
173 self.operator_code,
174 self.national_area_code,
175 self.core_number,
176 ] {
177 buf[pos..pos + field.len()].copy_from_slice(field);
178 pos += field.len();
179 }
180 Ok(len)
181 }
182}
183impl<'a> crate::traits::DescriptorDef<'a> for TelephoneDescriptor<'a> {
184 const TAG: u8 = TAG;
185 const NAME: &'static str = "TELEPHONE";
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 fn sample() -> TelephoneDescriptor<'static> {
193 TelephoneDescriptor {
194 foreign_availability: true,
195 connection_type: 0x05,
196 country_prefix: b"44",
197 international_area_code: b"171",
198 operator_code: b"01",
199 national_area_code: b"207",
200 core_number: b"123456",
201 }
202 }
203
204 #[test]
205 fn parse_extracts_all_fields() {
206 let d = sample();
207 let mut buf = vec![0u8; d.serialized_len()];
208 d.serialize_into(&mut buf).unwrap();
209 let p = TelephoneDescriptor::parse(&buf).unwrap();
210 assert!(p.foreign_availability);
211 assert_eq!(p.connection_type, 0x05);
212 assert_eq!(p.country_prefix, b"44");
213 assert_eq!(p.international_area_code, b"171");
214 assert_eq!(p.operator_code, b"01");
215 assert_eq!(p.national_area_code, b"207");
216 assert_eq!(p.core_number, b"123456");
217 }
218
219 #[test]
220 fn parse_minimal_no_chars() {
221 let bytes = [TAG, 3, 0x20, 0x00, 0x00];
223 let d = TelephoneDescriptor::parse(&bytes).unwrap();
224 assert!(d.foreign_availability);
225 assert!(d.country_prefix.is_empty());
226 assert!(d.core_number.is_empty());
227 }
228
229 #[test]
230 fn parse_rejects_wrong_tag() {
231 assert!(matches!(
232 TelephoneDescriptor::parse(&[0x58, 3, 0, 0, 0]).unwrap_err(),
233 Error::InvalidDescriptor { tag: 0x58, .. }
234 ));
235 }
236
237 #[test]
238 fn parse_rejects_short_buffer() {
239 let bytes = [TAG, 3, 0x20, 0x00];
241 assert!(matches!(
242 TelephoneDescriptor::parse(&bytes).unwrap_err(),
243 Error::BufferTooShort { .. }
244 ));
245 }
246
247 #[test]
248 fn parse_rejects_length_below_fixed() {
249 let bytes = [TAG, 2, 0x20, 0x00];
250 assert!(matches!(
251 TelephoneDescriptor::parse(&bytes).unwrap_err(),
252 Error::InvalidDescriptor { tag: TAG, .. }
253 ));
254 }
255
256 #[test]
257 fn parse_rejects_char_lengths_overrun() {
258 let bytes = [TAG, 3, 0x20, 0x60, 0x00];
261 assert!(matches!(
262 TelephoneDescriptor::parse(&bytes).unwrap_err(),
263 Error::InvalidDescriptor { tag: TAG, .. }
264 ));
265 }
266
267 #[test]
268 fn serialize_round_trip() {
269 let d = sample();
270 let mut buf = vec![0u8; d.serialized_len()];
271 d.serialize_into(&mut buf).unwrap();
272 assert_eq!(TelephoneDescriptor::parse(&buf).unwrap(), d);
273 }
274
275 #[test]
276 fn serialize_emits_reserved_ones() {
277 let d = TelephoneDescriptor {
278 foreign_availability: false,
279 connection_type: 0,
280 country_prefix: b"",
281 international_area_code: b"",
282 operator_code: b"",
283 national_area_code: b"",
284 core_number: b"",
285 };
286 let mut buf = vec![0u8; d.serialized_len()];
287 d.serialize_into(&mut buf).unwrap();
288 assert_eq!(buf[2] & BYTE0_RESERVED, BYTE0_RESERVED);
290 assert_eq!(buf[3] & BYTE1_RESERVED, BYTE1_RESERVED);
291 assert_eq!(buf[4] & BYTE2_RESERVED, BYTE2_RESERVED);
292 }
293
294 #[test]
295 fn serialize_rejects_over_range_field() {
296 let d = TelephoneDescriptor {
298 foreign_availability: false,
299 connection_type: 0,
300 country_prefix: b"",
301 international_area_code: b"",
302 operator_code: b"1234",
303 national_area_code: b"",
304 core_number: b"",
305 };
306 let mut buf = vec![0u8; d.serialized_len()];
307 assert!(matches!(
308 d.serialize_into(&mut buf).unwrap_err(),
309 Error::InvalidDescriptor { tag: TAG, .. }
310 ));
311 }
312
313 #[cfg(feature = "serde")]
314 #[test]
315 fn serde_serialize_stable() {
316 let d = sample();
321 let json = serde_json::to_string(&d).unwrap();
322 assert!(json.contains("connection_type"));
323 assert!(json.contains("foreign_availability"));
324 assert_eq!(json, serde_json::to_string(&sample()).unwrap());
325 }
326}