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