Skip to main content

dvb_si/descriptors/
short_event.rs

1//! Short Event Descriptor — ETSI EN 300 468 §6.2.37 (tag 0x4D).
2//!
3//! Carried inside EIT. Gives the event's title and brief description in a
4//! single language.
5
6use crate::error::{Error, Result};
7use crate::text::{DvbText, LangCode};
8use crate::traits::Descriptor;
9use dvb_common::{Parse, Serialize};
10
11/// Descriptor tag for short_event_descriptor.
12pub const TAG: u8 = 0x4D;
13const HEADER_LEN: usize = 2;
14const LANG_LEN: usize = 3;
15
16/// Short Event Descriptor.
17#[derive(Debug, Clone, PartialEq, Eq)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize))]
19pub struct ShortEventDescriptor<'a> {
20    /// ISO 639-2 language code of the event name / text.
21    pub language_code: LangCode,
22    /// DVB Annex-A encoded event name.
23    pub event_name: DvbText<'a>,
24    /// DVB Annex-A encoded brief description.
25    pub text: DvbText<'a>,
26}
27
28impl<'a> Parse<'a> for ShortEventDescriptor<'a> {
29    type Error = crate::error::Error;
30    fn parse(bytes: &'a [u8]) -> Result<Self> {
31        if bytes.len() < HEADER_LEN {
32            return Err(Error::BufferTooShort {
33                need: HEADER_LEN,
34                have: bytes.len(),
35                what: "ShortEventDescriptor header",
36            });
37        }
38        if bytes[0] != TAG {
39            return Err(Error::InvalidDescriptor {
40                tag: bytes[0],
41                reason: "unexpected tag for short_event_descriptor",
42            });
43        }
44        let length = bytes[1] as usize;
45        let end = HEADER_LEN + length;
46        if bytes.len() < end {
47            return Err(Error::BufferTooShort {
48                need: end,
49                have: bytes.len(),
50                what: "ShortEventDescriptor body",
51            });
52        }
53        if length < LANG_LEN + 2 {
54            return Err(Error::InvalidDescriptor {
55                tag: TAG,
56                reason: "short_event_descriptor body shorter than minimum 5 bytes",
57            });
58        }
59        let body_start = HEADER_LEN;
60        let language_code = LangCode([
61            bytes[body_start],
62            bytes[body_start + 1],
63            bytes[body_start + 2],
64        ]);
65        let name_len_pos = body_start + LANG_LEN;
66        let name_len = bytes[name_len_pos] as usize;
67        let name_start = name_len_pos + 1;
68        let name_end = name_start + name_len;
69        if name_end + 1 > end {
70            return Err(Error::InvalidDescriptor {
71                tag: TAG,
72                reason: "event_name_length runs past descriptor end",
73            });
74        }
75        let event_name = DvbText::new(&bytes[name_start..name_end]);
76        let text_len = bytes[name_end] as usize;
77        let text_start = name_end + 1;
78        let text_end = text_start + text_len;
79        if text_end > end {
80            return Err(Error::InvalidDescriptor {
81                tag: TAG,
82                reason: "text_length runs past descriptor end",
83            });
84        }
85        let text = DvbText::new(&bytes[text_start..text_end]);
86        Ok(Self {
87            language_code,
88            event_name,
89            text,
90        })
91    }
92}
93
94impl Serialize for ShortEventDescriptor<'_> {
95    type Error = crate::error::Error;
96    fn serialized_len(&self) -> usize {
97        HEADER_LEN + LANG_LEN + 1 + self.event_name.len() + 1 + self.text.len()
98    }
99
100    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
101        let len = self.serialized_len();
102        if buf.len() < len {
103            return Err(Error::OutputBufferTooSmall {
104                need: len,
105                have: buf.len(),
106            });
107        }
108        buf[0] = TAG;
109        buf[1] = (len - HEADER_LEN) as u8;
110        buf[2..5].copy_from_slice(&self.language_code.0);
111        buf[5] = self.event_name.len() as u8;
112        let n_start = 6;
113        let n_end = n_start + self.event_name.len();
114        buf[n_start..n_end].copy_from_slice(self.event_name.raw());
115        buf[n_end] = self.text.len() as u8;
116        let t_start = n_end + 1;
117        buf[t_start..t_start + self.text.len()].copy_from_slice(self.text.raw());
118        Ok(len)
119    }
120}
121
122impl<'a> Descriptor<'a> for ShortEventDescriptor<'a> {
123    const TAG: u8 = TAG;
124    fn descriptor_length(&self) -> u8 {
125        (self.serialized_len() - HEADER_LEN) as u8
126    }
127}
128
129impl<'a> crate::traits::DescriptorDef<'a> for ShortEventDescriptor<'a> {
130    const TAG: u8 = TAG;
131    const NAME: &'static str = "SHORT_EVENT";
132}
133
134#[cfg(test)]
135mod tests {
136    use super::*;
137
138    #[test]
139    fn parse_extracts_all_fields() {
140        let bytes = [
141            TAG, 0x0C, b'e', b'n', b'g', 4, b'N', b'e', b'w', b's', 3, b'L', b'i', b'v',
142        ];
143        let d = ShortEventDescriptor::parse(&bytes).unwrap();
144        assert_eq!(d.language_code, LangCode(*b"eng"));
145        assert_eq!(d.event_name.raw(), b"News");
146        assert_eq!(d.text.raw(), b"Liv");
147    }
148
149    #[test]
150    fn parse_rejects_wrong_tag() {
151        let err = ShortEventDescriptor::parse(&[0x4E, 0]).unwrap_err();
152        assert!(matches!(err, Error::InvalidDescriptor { tag: 0x4E, .. }));
153    }
154
155    #[test]
156    fn parse_rejects_short_buffer() {
157        let err = ShortEventDescriptor::parse(&[TAG]).unwrap_err();
158        assert!(matches!(err, Error::BufferTooShort { .. }));
159    }
160
161    #[test]
162    fn parse_rejects_body_too_short_for_minimum() {
163        // Body of length 3 has language code but no event_name length field.
164        let err = ShortEventDescriptor::parse(&[TAG, 3, b'e', b'n', b'g']).unwrap_err();
165        assert!(matches!(err, Error::InvalidDescriptor { .. }));
166    }
167
168    #[test]
169    fn parse_rejects_name_length_overrun() {
170        // body length 5 = lang(3) + name_len(1) + 1 byte of name/text.
171        // Set name_len = 100 — bigger than the remaining body.
172        let bytes = [TAG, 5, b'e', b'n', b'g', 100, 0];
173        let err = ShortEventDescriptor::parse(&bytes).unwrap_err();
174        assert!(matches!(err, Error::InvalidDescriptor { .. }));
175    }
176
177    #[test]
178    fn empty_event_name_and_text_valid() {
179        let bytes = [TAG, 5, b'e', b'n', b'g', 0, 0];
180        let d = ShortEventDescriptor::parse(&bytes).unwrap();
181        assert!(d.event_name.raw().is_empty());
182        assert!(d.text.raw().is_empty());
183    }
184
185    #[test]
186    fn serialize_round_trip() {
187        let d = ShortEventDescriptor {
188            language_code: LangCode(*b"fra"),
189            event_name: DvbText::new(b"Journal"),
190            text: DvbText::new(b"20h"),
191        };
192        let mut buf = vec![0u8; d.serialized_len()];
193        d.serialize_into(&mut buf).unwrap();
194        let re = ShortEventDescriptor::parse(&buf).unwrap();
195        assert_eq!(d, re);
196    }
197
198    #[test]
199    fn descriptor_length_matches_payload() {
200        let d = ShortEventDescriptor {
201            language_code: LangCode(*b"eng"),
202            event_name: DvbText::new(b"ABC"),
203            text: DvbText::new(b"DE"),
204        };
205        // 3 lang + 1 name_len + 3 name + 1 text_len + 2 text = 10
206        assert_eq!(d.descriptor_length(), 10);
207    }
208}