dvb_si/descriptors/extension/
registry.rs1use alloc::boxed::Box;
22use alloc::collections::BTreeMap;
23
24use super::{ExtensionBodyDef, ExtensionDescriptor, validate_and_split};
25use crate::error::Result;
26
27broadcast_common::declare_object_trait! {
28 pub trait ExtensionObject, "extension descriptor"
36}
37
38pub(crate) type CustomParse =
46 Box<dyn for<'a> Fn(&'a [u8]) -> Result<Box<dyn ExtensionObject>> + Send + Sync>;
47
48#[derive(Default)]
63pub struct ExtensionRegistry {
64 custom: BTreeMap<u8, CustomParse>,
65}
66
67impl ExtensionRegistry {
68 #[must_use]
70 pub fn new() -> Self {
71 Self::default()
72 }
73
74 #[must_use]
77 pub fn has_custom(&self, tag_extension: u8) -> bool {
78 self.custom.contains_key(&tag_extension)
79 }
80
81 pub fn register<T>(&mut self) -> &mut Self
93 where
94 T: for<'a> ExtensionBodyDef<'a> + ExtensionObject + 'static,
95 {
96 let tag_ext = T::TAG_EXTENSION;
97 self.custom.insert(
98 tag_ext,
99 Box::new(|sel| {
100 Ok(Box::new(<T as broadcast_common::Parse>::parse(sel)?)
101 as Box<dyn ExtensionObject>)
102 }),
103 );
104 self
105 }
106
107 pub fn parse_body<'a>(
111 &self,
112 tag_extension: u8,
113 selector: &'a [u8],
114 ) -> Result<RegisteredExtension<'a>> {
115 if let Some(parse_fn) = self.custom.get(&tag_extension) {
116 let value = parse_fn(selector)?;
117 Ok(RegisteredExtension::Custom {
118 tag_extension,
119 value,
120 })
121 } else {
122 let body = super::parse_body(tag_extension, selector)?;
123 Ok(RegisteredExtension::Builtin(ExtensionDescriptor {
124 tag_extension,
125 body,
126 }))
127 }
128 }
129
130 pub fn parse<'a>(&self, bytes: &'a [u8]) -> Result<RegisteredExtension<'a>> {
138 let (tag_extension, sel) = validate_and_split(bytes)?;
139 self.parse_body(tag_extension, sel)
140 }
141}
142
143#[derive(Debug)]
149#[cfg_attr(feature = "serde", derive(serde::Serialize))]
150#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
151#[non_exhaustive]
152pub enum RegisteredExtension<'a> {
153 Builtin(super::ExtensionDescriptor<'a>),
156 Custom {
158 tag_extension: u8,
160 #[cfg_attr(feature = "serde", serde(serialize_with = "serialize_erased"))]
163 value: Box<dyn ExtensionObject>,
164 },
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170 use crate::descriptors::extension::{ExtensionBodyDef, TAG, TAG_EXTENSION_LEN};
171 use crate::error::Error;
172 use alloc::{vec, vec::Vec};
173
174 const TEST_TAG_EXTENSION: u8 = 0x40;
175
176 #[derive(Debug, PartialEq, Eq)]
177 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
178 struct MyExtBody {
179 payload: Vec<u8>,
180 }
181
182 impl ExtensionBodyDef<'_> for MyExtBody {
183 const TAG_EXTENSION: u8 = TEST_TAG_EXTENSION;
184 const NAME: &'static str = "MY_EXT_BODY";
185 }
186
187 impl<'a> broadcast_common::Parse<'a> for MyExtBody {
188 type Error = crate::error::Error;
189 fn parse(sel: &'a [u8]) -> Result<Self> {
190 Ok(Self {
191 payload: sel.to_vec(),
192 })
193 }
194 }
195
196 fn wrap_ext(tag_ext: u8, sel: &[u8]) -> Vec<u8> {
197 let mut v = vec![TAG, (sel.len() + TAG_EXTENSION_LEN) as u8, tag_ext];
198 v.extend_from_slice(sel);
199 v
200 }
201
202 #[test]
203 fn custom_extension_parsed_and_downcastable() {
204 let mut reg = ExtensionRegistry::new();
205 reg.register::<MyExtBody>();
206
207 let sel = [0xDE, 0xAD, 0xBE];
208 let bytes = wrap_ext(TEST_TAG_EXTENSION, &sel);
209 let re = reg.parse(&bytes).unwrap();
210 match re {
211 RegisteredExtension::Custom {
212 tag_extension,
213 value,
214 } => {
215 assert_eq!(tag_extension, TEST_TAG_EXTENSION);
216 let concrete = value
217 .downcast_ref::<MyExtBody>()
218 .expect("downcast should succeed");
219 assert_eq!(concrete.payload, sel);
220 }
221 other => panic!("expected Custom, got {other:?}"),
222 }
223 }
224
225 #[test]
226 fn unregistered_tag_extension_yields_builtin() {
227 use crate::descriptors::extension::ExtensionBody;
228 let reg = ExtensionRegistry::new();
229 let d = crate::descriptors::extension::ExtensionDescriptor {
231 tag_extension: 0x0B,
232 body: ExtensionBody::ServiceRelocated(
233 crate::descriptors::extension::ServiceRelocated {
234 old_original_network_id: 1,
235 old_transport_stream_id: 2,
236 old_service_id: 3,
237 },
238 ),
239 };
240 let mut buf = vec![0u8; d.serialized_len()];
241 use broadcast_common::Serialize;
242 d.serialize_into(&mut buf).unwrap();
243
244 let re = reg.parse(&buf).unwrap();
245 match re {
246 RegisteredExtension::Builtin(d) => {
247 assert_eq!(d.tag_extension, 0x0B);
248 assert!(matches!(d.body, ExtensionBody::ServiceRelocated(_)));
249 }
250 other => panic!("expected Builtin, got {other:?}"),
251 }
252 }
253
254 #[test]
255 fn unknown_tag_extension_yields_builtin_raw() {
256 use crate::descriptors::extension::ExtensionBody;
257 let reg = ExtensionRegistry::new();
258 let sel = [0xAA, 0xBB];
259 let bytes = wrap_ext(0xFE, &sel);
260 let re = reg.parse(&bytes).unwrap();
261 match re {
262 RegisteredExtension::Builtin(d) => {
263 assert_eq!(d.tag_extension, 0xFE);
264 assert!(matches!(d.body, ExtensionBody::Raw(b) if b == sel));
265 }
266 other => panic!("expected Builtin, got {other:?}"),
267 }
268 }
269
270 #[test]
271 fn parse_rejects_wrong_tag() {
272 let reg = ExtensionRegistry::new();
273 let raw = [0x43, 1, 0x04];
274 assert!(matches!(
275 reg.parse(&raw).unwrap_err(),
276 Error::InvalidDescriptor { tag: 0x43, .. }
277 ));
278 }
279
280 #[test]
281 fn parse_rejects_short_buffer() {
282 let reg = ExtensionRegistry::new();
283 let raw = [TAG];
284 assert!(matches!(
285 reg.parse(&raw).unwrap_err(),
286 Error::BufferTooShort(..)
287 ));
288 }
289
290 #[test]
291 fn parse_rejects_empty_body() {
292 let reg = ExtensionRegistry::new();
293 let raw = [TAG, 0];
294 assert!(matches!(
295 reg.parse(&raw).unwrap_err(),
296 Error::InvalidDescriptor { tag: TAG, .. }
297 ));
298 }
299
300 #[cfg(feature = "serde")]
301 #[test]
302 fn custom_variant_serializes_via_erased_serde() {
303 let mut reg = ExtensionRegistry::new();
304 reg.register::<MyExtBody>();
305
306 let bytes = wrap_ext(TEST_TAG_EXTENSION, &[0x01, 0x02]);
307 let re = reg.parse(&bytes).unwrap();
308 let json = serde_json::to_value(&re).unwrap();
309 let custom = json.get("custom").expect("expected 'custom' key");
310 assert_eq!(custom["tag_extension"], TEST_TAG_EXTENSION as u64);
311 assert_eq!(custom["value"]["payload"], serde_json::json!([1, 2]));
312 }
313}