dvb_si/descriptors/
service.rs1use crate::error::{Error, Result};
7use crate::text::DvbText;
8use crate::traits::Descriptor;
9use dvb_common::{Parse, Serialize};
10
11pub const TAG: u8 = 0x48;
13const HEADER_LEN: usize = 2;
14
15#[derive(Debug, Clone, PartialEq, Eq)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize))]
18#[cfg_attr(feature = "yoke", derive(yoke::Yokeable))]
19pub struct ServiceDescriptor<'a> {
20 pub service_type: u8,
22 pub provider_name: DvbText<'a>,
24 pub service_name: DvbText<'a>,
26}
27
28impl<'a> Parse<'a> for ServiceDescriptor<'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: "ServiceDescriptor header",
36 });
37 }
38 if bytes[0] != TAG {
39 return Err(Error::InvalidDescriptor {
40 tag: bytes[0],
41 reason: "unexpected tag for service_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: "ServiceDescriptor body",
51 });
52 }
53 if length < 3 {
54 return Err(Error::InvalidDescriptor {
55 tag: TAG,
56 reason: "service_descriptor body too short for service_type + two length fields",
57 });
58 }
59 let service_type = bytes[HEADER_LEN];
60 let provider_len = bytes[HEADER_LEN + 1] as usize;
61 let provider_end = HEADER_LEN + 2 + provider_len;
62 if provider_end + 1 > end {
63 return Err(Error::InvalidDescriptor {
64 tag: TAG,
65 reason: "service_provider_name_length runs past descriptor end",
66 });
67 }
68 let provider_name = DvbText::new(&bytes[HEADER_LEN + 2..provider_end]);
69 let service_len = bytes[provider_end] as usize;
70 let service_end = provider_end + 1 + service_len;
71 if service_end > end {
72 return Err(Error::InvalidDescriptor {
73 tag: TAG,
74 reason: "service_name_length runs past descriptor end",
75 });
76 }
77 let service_name = DvbText::new(&bytes[provider_end + 1..service_end]);
78 Ok(Self {
79 service_type,
80 provider_name,
81 service_name,
82 })
83 }
84}
85
86impl Serialize for ServiceDescriptor<'_> {
87 type Error = crate::error::Error;
88 fn serialized_len(&self) -> usize {
89 HEADER_LEN + 1 + 1 + self.provider_name.len() + 1 + self.service_name.len()
90 }
91
92 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
93 let len = self.serialized_len();
94 if buf.len() < len {
95 return Err(Error::OutputBufferTooSmall {
96 need: len,
97 have: buf.len(),
98 });
99 }
100 buf[0] = TAG;
101 buf[1] = (len - HEADER_LEN) as u8;
102 buf[2] = self.service_type;
103 buf[3] = self.provider_name.len() as u8;
104 let p_start = 4;
105 let p_end = p_start + self.provider_name.len();
106 buf[p_start..p_end].copy_from_slice(self.provider_name.raw());
107 buf[p_end] = self.service_name.len() as u8;
108 let s_start = p_end + 1;
109 buf[s_start..s_start + self.service_name.len()].copy_from_slice(self.service_name.raw());
110 Ok(len)
111 }
112}
113
114impl<'a> Descriptor<'a> for ServiceDescriptor<'a> {
115 const TAG: u8 = TAG;
116 fn descriptor_length(&self) -> u8 {
117 (self.serialized_len() - HEADER_LEN) as u8
118 }
119}
120
121impl<'a> crate::traits::DescriptorDef<'a> for ServiceDescriptor<'a> {
122 const TAG: u8 = TAG;
123 const NAME: &'static str = "SERVICE";
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn parse_extracts_all_fields() {
132 let bytes = [
134 TAG, 10, 0x01, 4, b'E', b'U', b'T', b'E', 3, b'T', b'F', b'1',
135 ];
136 let d = ServiceDescriptor::parse(&bytes).unwrap();
137 assert_eq!(d.service_type, 1);
138 assert_eq!(d.provider_name.raw(), b"EUTE");
139 assert_eq!(d.service_name.raw(), b"TF1");
140 }
141
142 #[test]
143 fn parse_rejects_wrong_tag() {
144 let err = ServiceDescriptor::parse(&[0x49, 0]).unwrap_err();
145 assert!(matches!(err, Error::InvalidDescriptor { tag: 0x49, .. }));
146 }
147
148 #[test]
149 fn parse_rejects_short_header() {
150 let err = ServiceDescriptor::parse(&[TAG]).unwrap_err();
151 assert!(matches!(err, Error::BufferTooShort { .. }));
152 }
153
154 #[test]
155 fn parse_rejects_truncated_body() {
156 let err = ServiceDescriptor::parse(&[TAG, 5, 0x01, 0xFF]).unwrap_err();
157 assert!(matches!(err, Error::BufferTooShort { .. }));
158 }
159
160 #[test]
161 fn parse_rejects_provider_length_overrun() {
162 let bytes = [TAG, 5, 0x01, 100, b'A', b'B', b'C'];
164 let err = ServiceDescriptor::parse(&bytes).unwrap_err();
165 assert!(matches!(err, Error::InvalidDescriptor { .. }));
166 }
167
168 #[test]
169 fn empty_provider_and_service_names_valid() {
170 let bytes = [TAG, 3, 0x01, 0, 0];
171 let d = ServiceDescriptor::parse(&bytes).unwrap();
172 assert!(d.provider_name.raw().is_empty());
173 assert!(d.service_name.raw().is_empty());
174 }
175
176 #[test]
177 fn serialize_round_trip() {
178 let d = ServiceDescriptor {
179 service_type: 0x19,
180 provider_name: DvbText::new(b"BBC"),
181 service_name: DvbText::new(b"BBC ONE HD"),
182 };
183 let mut buf = vec![0u8; d.serialized_len()];
184 d.serialize_into(&mut buf).unwrap();
185 let re = ServiceDescriptor::parse(&buf).unwrap();
186 assert_eq!(d, re);
187 }
188
189 #[test]
190 fn descriptor_length_matches_payload() {
191 let d = ServiceDescriptor {
192 service_type: 1,
193 provider_name: DvbText::new(b"AA"),
194 service_name: DvbText::new(b"BBB"),
195 };
196 assert_eq!(d.descriptor_length(), 8);
198 }
199}