Skip to main content

dvb_si/descriptors/
telephone.rs

1//! Telephone Descriptor — ETSI EN 300 468 §6.2.42 (tag 0x57).
2//!
3//! Table 100 (PDF p. 107). Carries a telephone number (for IRD-initiated
4//! return-channel calls) split into five length-prefixed character fields.
5//!
6//! Fixed 3-byte header:
7//!   byte0: reserved(2) | foreign_availability(1) | connection_type(5)
8//!   byte1: reserved(1) | country_prefix_length(2)
9//!          | international_area_code_length(3) | operator_code_length(2)
10//!   byte2: reserved(1) | national_area_code_length(3) | core_number_length(4)
11//! followed by the five char loops, each `<field>_length` ISO 8859-1 bytes.
12//!
13//! Each *_char field is kept as a borrowed `&[u8]`; the four/three/two-bit
14//! length fields are derived from slice length on serialize and ERROR on
15//! over-range rather than truncating (crate idiom).
16
17use crate::error::{Error, Result};
18use crate::traits::Descriptor;
19use dvb_common::{Parse, Serialize};
20
21/// Descriptor tag for telephone_descriptor.
22pub const TAG: u8 = 0x57;
23const HEADER_LEN: usize = 2;
24/// Three fixed flag/length bytes precede the char loops.
25const FIXED_LEN: usize = 3;
26
27const FOREIGN_AVAIL_MASK: u8 = 0x20; // byte0 bit 5
28const CONNECTION_TYPE_MASK: u8 = 0x1F; // byte0 bits 0..4
29const BYTE0_RESERVED: u8 = 0xC0; // byte0 bits 7..6 reserved -> 1s
30const BYTE1_RESERVED: u8 = 0x80; // byte1 bit 7 reserved -> 1
31const BYTE2_RESERVED: u8 = 0x80; // byte2 bit 7 reserved -> 1
32
33// Maxima implied by each length field's bit-width.
34const MAX_COUNTRY_PREFIX: usize = 0x03; // 2 bits
35const MAX_INTL_AREA: usize = 0x07; // 3 bits
36const MAX_OPERATOR: usize = 0x03; // 2 bits
37const MAX_NATIONAL_AREA: usize = 0x07; // 3 bits
38const MAX_CORE_NUMBER: usize = 0x0F; // 4 bits
39
40/// Telephone Descriptor.
41#[derive(Debug, Clone, PartialEq, Eq)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43#[cfg_attr(feature = "serde", serde(bound(deserialize = "'de: 'a")))]
44pub struct TelephoneDescriptor<'a> {
45    /// When true, the number may be dialled from outside the prefix's country.
46    pub foreign_availability: bool,
47    /// 5-bit connection_type (meaning out of scope of EN 300 468).
48    pub connection_type: u8,
49    /// country_prefix_char bytes (≤ 3, ISO 8859-1).
50    #[cfg_attr(feature = "serde", serde(borrow))]
51    pub country_prefix: &'a [u8],
52    /// international_area_code_char bytes (≤ 7).
53    #[cfg_attr(feature = "serde", serde(borrow))]
54    pub international_area_code: &'a [u8],
55    /// operator_code_char bytes (≤ 3).
56    #[cfg_attr(feature = "serde", serde(borrow))]
57    pub operator_code: &'a [u8],
58    /// national_area_code_char bytes (≤ 7).
59    #[cfg_attr(feature = "serde", serde(borrow))]
60    pub national_area_code: &'a [u8],
61    /// core_number_char bytes (≤ 15).
62    #[cfg_attr(feature = "serde", serde(borrow))]
63    pub core_number: &'a [u8],
64}
65
66impl<'a> Parse<'a> for TelephoneDescriptor<'a> {
67    type Error = crate::error::Error;
68    fn parse(bytes: &'a [u8]) -> Result<Self> {
69        if bytes.len() < HEADER_LEN {
70            return Err(Error::BufferTooShort {
71                need: HEADER_LEN,
72                have: bytes.len(),
73                what: "TelephoneDescriptor header",
74            });
75        }
76        if bytes[0] != TAG {
77            return Err(Error::InvalidDescriptor {
78                tag: bytes[0],
79                reason: "unexpected tag for telephone_descriptor",
80            });
81        }
82        let length = bytes[1] as usize;
83        if length < FIXED_LEN {
84            return Err(Error::InvalidDescriptor {
85                tag: TAG,
86                reason: "telephone_descriptor length too short for fixed fields",
87            });
88        }
89        let end = HEADER_LEN + length;
90        if bytes.len() < end {
91            return Err(Error::BufferTooShort {
92                need: end,
93                have: bytes.len(),
94                what: "TelephoneDescriptor body",
95            });
96        }
97        let body = &bytes[HEADER_LEN..end];
98        // reserved bits ignored on parse (EN 300 468 §5.1).
99        let foreign_availability = body[0] & FOREIGN_AVAIL_MASK != 0;
100        let connection_type = body[0] & CONNECTION_TYPE_MASK;
101        let country_prefix_length = ((body[1] >> 5) & 0x03) as usize;
102        let international_area_code_length = ((body[1] >> 2) & 0x07) as usize;
103        let operator_code_length = (body[1] & 0x03) as usize;
104        let national_area_code_length = ((body[2] >> 4) & 0x07) as usize;
105        let core_number_length = (body[2] & 0x0F) as usize;
106        let total_chars = country_prefix_length
107            + international_area_code_length
108            + operator_code_length
109            + national_area_code_length
110            + core_number_length;
111        if FIXED_LEN + total_chars > body.len() {
112            return Err(Error::InvalidDescriptor {
113                tag: TAG,
114                reason: "sum of telephone char-field lengths exceeds descriptor body",
115            });
116        }
117        let mut pos = FIXED_LEN;
118        let mut take = |n: usize| {
119            let s = &body[pos..pos + n];
120            pos += n;
121            s
122        };
123        let country_prefix = take(country_prefix_length);
124        let international_area_code = take(international_area_code_length);
125        let operator_code = take(operator_code_length);
126        let national_area_code = take(national_area_code_length);
127        let core_number = take(core_number_length);
128        Ok(Self {
129            foreign_availability,
130            connection_type,
131            country_prefix,
132            international_area_code,
133            operator_code,
134            national_area_code,
135            core_number,
136        })
137    }
138}
139
140impl Serialize for TelephoneDescriptor<'_> {
141    type Error = crate::error::Error;
142    fn serialized_len(&self) -> usize {
143        HEADER_LEN
144            + FIXED_LEN
145            + self.country_prefix.len()
146            + self.international_area_code.len()
147            + self.operator_code.len()
148            + self.national_area_code.len()
149            + self.core_number.len()
150    }
151
152    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
153        let len = self.serialized_len();
154        if buf.len() < len {
155            return Err(Error::OutputBufferTooSmall {
156                need: len,
157                have: buf.len(),
158            });
159        }
160        // Per-field length fields: error on over-range rather than truncate.
161        if self.country_prefix.len() > MAX_COUNTRY_PREFIX
162            || self.international_area_code.len() > MAX_INTL_AREA
163            || self.operator_code.len() > MAX_OPERATOR
164            || self.national_area_code.len() > MAX_NATIONAL_AREA
165            || self.core_number.len() > MAX_CORE_NUMBER
166        {
167            return Err(Error::InvalidDescriptor {
168                tag: TAG,
169                reason: "telephone char-field exceeds its length-field capacity",
170            });
171        }
172        buf[0] = TAG;
173        buf[1] = (len - HEADER_LEN) as u8;
174        // byte0: reserved(2)=1s | foreign_availability(1) | connection_type(5)
175        buf[2] = BYTE0_RESERVED
176            | if self.foreign_availability {
177                FOREIGN_AVAIL_MASK
178            } else {
179                0
180            }
181            | (self.connection_type & CONNECTION_TYPE_MASK);
182        // byte1: reserved(1)=1 | cp_len(2) | iac_len(3) | op_len(2)
183        buf[3] = BYTE1_RESERVED
184            | ((self.country_prefix.len() as u8 & 0x03) << 5)
185            | ((self.international_area_code.len() as u8 & 0x07) << 2)
186            | (self.operator_code.len() as u8 & 0x03);
187        // byte2: reserved(1)=1 | nac_len(3) | core_len(4)
188        buf[4] = BYTE2_RESERVED
189            | ((self.national_area_code.len() as u8 & 0x07) << 4)
190            | (self.core_number.len() as u8 & 0x0F);
191        let mut pos = HEADER_LEN + FIXED_LEN;
192        for field in [
193            self.country_prefix,
194            self.international_area_code,
195            self.operator_code,
196            self.national_area_code,
197            self.core_number,
198        ] {
199            buf[pos..pos + field.len()].copy_from_slice(field);
200            pos += field.len();
201        }
202        Ok(len)
203    }
204}
205
206impl<'a> Descriptor<'a> for TelephoneDescriptor<'a> {
207    const TAG: u8 = TAG;
208    fn descriptor_length(&self) -> u8 {
209        (self.serialized_len() - HEADER_LEN) as u8
210    }
211}
212
213impl<'a> crate::traits::DescriptorDef<'a> for TelephoneDescriptor<'a> {
214    const TAG: u8 = TAG;
215    const NAME: &'static str = "TELEPHONE";
216}
217
218#[cfg(test)]
219mod tests {
220    use super::*;
221
222    fn sample() -> TelephoneDescriptor<'static> {
223        TelephoneDescriptor {
224            foreign_availability: true,
225            connection_type: 0x05,
226            country_prefix: b"44",
227            international_area_code: b"171",
228            operator_code: b"01",
229            national_area_code: b"207",
230            core_number: b"123456",
231        }
232    }
233
234    #[test]
235    fn parse_extracts_all_fields() {
236        let d = sample();
237        let mut buf = vec![0u8; d.serialized_len()];
238        d.serialize_into(&mut buf).unwrap();
239        let p = TelephoneDescriptor::parse(&buf).unwrap();
240        assert!(p.foreign_availability);
241        assert_eq!(p.connection_type, 0x05);
242        assert_eq!(p.country_prefix, b"44");
243        assert_eq!(p.international_area_code, b"171");
244        assert_eq!(p.operator_code, b"01");
245        assert_eq!(p.national_area_code, b"207");
246        assert_eq!(p.core_number, b"123456");
247    }
248
249    #[test]
250    fn parse_minimal_no_chars() {
251        // length=3, all char-field lengths 0
252        let bytes = [TAG, 3, 0x20, 0x00, 0x00];
253        let d = TelephoneDescriptor::parse(&bytes).unwrap();
254        assert!(d.foreign_availability);
255        assert!(d.country_prefix.is_empty());
256        assert!(d.core_number.is_empty());
257    }
258
259    #[test]
260    fn parse_rejects_wrong_tag() {
261        assert!(matches!(
262            TelephoneDescriptor::parse(&[0x58, 3, 0, 0, 0]).unwrap_err(),
263            Error::InvalidDescriptor { tag: 0x58, .. }
264        ));
265    }
266
267    #[test]
268    fn parse_rejects_short_buffer() {
269        // declares length 3 but only 2 body bytes present
270        let bytes = [TAG, 3, 0x20, 0x00];
271        assert!(matches!(
272            TelephoneDescriptor::parse(&bytes).unwrap_err(),
273            Error::BufferTooShort { .. }
274        ));
275    }
276
277    #[test]
278    fn parse_rejects_length_below_fixed() {
279        let bytes = [TAG, 2, 0x20, 0x00];
280        assert!(matches!(
281            TelephoneDescriptor::parse(&bytes).unwrap_err(),
282            Error::InvalidDescriptor { tag: TAG, .. }
283        ));
284    }
285
286    #[test]
287    fn parse_rejects_char_lengths_overrun() {
288        // country_prefix_length=3 (byte1 bits 6..5 = 11) but body only has the
289        // 3 fixed bytes — the char loop would overrun.
290        let bytes = [TAG, 3, 0x20, 0x60, 0x00];
291        assert!(matches!(
292            TelephoneDescriptor::parse(&bytes).unwrap_err(),
293            Error::InvalidDescriptor { tag: TAG, .. }
294        ));
295    }
296
297    #[test]
298    fn serialize_round_trip() {
299        let d = sample();
300        let mut buf = vec![0u8; d.serialized_len()];
301        d.serialize_into(&mut buf).unwrap();
302        assert_eq!(TelephoneDescriptor::parse(&buf).unwrap(), d);
303    }
304
305    #[test]
306    fn serialize_emits_reserved_ones() {
307        let d = TelephoneDescriptor {
308            foreign_availability: false,
309            connection_type: 0,
310            country_prefix: b"",
311            international_area_code: b"",
312            operator_code: b"",
313            national_area_code: b"",
314            core_number: b"",
315        };
316        let mut buf = vec![0u8; d.serialized_len()];
317        d.serialize_into(&mut buf).unwrap();
318        // byte0 reserved bits 7..6 set; byte1 & byte2 reserved bit 7 set
319        assert_eq!(buf[2] & BYTE0_RESERVED, BYTE0_RESERVED);
320        assert_eq!(buf[3] & BYTE1_RESERVED, BYTE1_RESERVED);
321        assert_eq!(buf[4] & BYTE2_RESERVED, BYTE2_RESERVED);
322    }
323
324    #[test]
325    fn serialize_rejects_over_range_field() {
326        // operator_code length 4 exceeds its 2-bit field (max 3).
327        let d = TelephoneDescriptor {
328            foreign_availability: false,
329            connection_type: 0,
330            country_prefix: b"",
331            international_area_code: b"",
332            operator_code: b"1234",
333            national_area_code: b"",
334            core_number: b"",
335        };
336        let mut buf = vec![0u8; d.serialized_len()];
337        assert!(matches!(
338            d.serialize_into(&mut buf).unwrap_err(),
339            Error::InvalidDescriptor { tag: TAG, .. }
340        ));
341    }
342
343    #[cfg(feature = "serde")]
344    #[test]
345    fn serde_serialize_stable() {
346        // Borrowed-byte fields cannot deserialize from a JSON array (serde_json
347        // requires a borrowed-str for &[u8]); assert the Serialize half is
348        // stable, matching the other borrowed descriptors (e.g.
349        // content_identifier) in this crate.
350        let d = sample();
351        let json = serde_json::to_string(&d).unwrap();
352        assert!(json.contains("connection_type"));
353        assert!(json.contains("foreign_availability"));
354        assert_eq!(json, serde_json::to_string(&sample()).unwrap());
355    }
356}