use alloc::boxed::Box;
use alloc::collections::BTreeMap;
use core::any::Any;
#[cfg(not(feature = "serde"))]
pub trait PayloadObject: core::fmt::Debug + Any + Send + Sync {
fn as_any(&self) -> &dyn Any;
}
#[cfg(feature = "serde")]
pub trait PayloadObject: core::fmt::Debug + Any + Send + Sync + erased_serde::Serialize {
fn as_any(&self) -> &dyn Any;
}
#[cfg(not(feature = "serde"))]
impl<T> PayloadObject for T
where
T: core::fmt::Debug + Any + Send + Sync,
{
fn as_any(&self) -> &dyn Any {
self
}
}
#[cfg(feature = "serde")]
impl<T> PayloadObject for T
where
T: core::fmt::Debug + Any + Send + Sync + serde::Serialize,
{
fn as_any(&self) -> &dyn Any {
self
}
}
impl dyn PayloadObject {
#[must_use]
pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
self.as_any().downcast_ref::<T>()
}
#[must_use]
pub fn is<T: Any>(&self) -> bool {
self.as_any().is::<T>()
}
}
#[cfg(feature = "serde")]
#[allow(clippy::borrowed_box)]
pub(crate) fn serialize_erased<S: serde::Serializer>(
v: &Box<dyn PayloadObject>,
s: S,
) -> Result<S::Ok, S::Error> {
erased_serde::serialize(&**v, s)
}
pub(crate) type CustomParse =
Box<dyn for<'a> Fn(&'a [u8]) -> crate::Result<Box<dyn PayloadObject>> + Send + Sync>;
#[derive(Default)]
pub struct PayloadRegistry {
custom: BTreeMap<u8, CustomParse>,
}
impl PayloadRegistry {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn register<T>(&mut self) -> &mut Self
where
T: for<'a> crate::traits::PayloadDef<'a> + PayloadObject + 'static,
{
let packet_type = <T as crate::traits::PayloadDef<'static>>::PACKET_TYPE;
self.custom.insert(
packet_type,
Box::new(|b| {
Ok(Box::new(<T as dvb_common::Parse>::parse(b)?) as Box<dyn PayloadObject>)
}),
);
self
}
#[must_use]
pub(crate) fn lookup(&self, packet_type: u8) -> Option<&CustomParse> {
self.custom.get(&packet_type)
}
}
#[cfg(test)]
mod tests {
use super::*;
use dvb_common::Parse;
#[derive(Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
struct TestPayload {
val: u8,
}
const TEST_PACKET_TYPE: u8 = 0x40;
impl<'a> Parse<'a> for TestPayload {
type Error = crate::Error;
fn parse(bytes: &'a [u8]) -> crate::Result<Self> {
if bytes.is_empty() {
return Err(crate::Error::BufferTooShort {
need: 1,
have: 0,
what: "TestPayload",
});
}
Ok(Self { val: bytes[0] })
}
}
impl<'a> crate::traits::PayloadDef<'a> for TestPayload {
const PACKET_TYPE: u8 = TEST_PACKET_TYPE;
const NAME: &'static str = "TEST_PAYLOAD";
}
#[test]
fn register_and_dispatch_returns_other() {
let mut reg = PayloadRegistry::new();
reg.register::<TestPayload>();
let bytes = [0x42u8];
let result = crate::payload::AnyPayload::dispatch_with(®, TEST_PACKET_TYPE, &bytes);
let parsed = result.unwrap().unwrap();
match parsed {
crate::payload::AnyPayload::Other {
packet_type,
ref value,
} => {
assert_eq!(packet_type, TEST_PACKET_TYPE);
let tp = value.downcast_ref::<TestPayload>().unwrap();
assert_eq!(tp.val, 0x42);
}
_ => panic!("expected Other, got {parsed:?}"),
}
}
#[test]
fn dispatch_with_falls_back_to_builtin() {
let reg = PayloadRegistry::new();
let bytes = [0x00, 0x00, 0x00]; let result = crate::payload::AnyPayload::dispatch_with(®, 0x00, &bytes);
let parsed = result.unwrap().unwrap();
assert!(
matches!(parsed, crate::payload::AnyPayload::Bbframe(_)),
"expected Bbframe, got {parsed:?}"
);
}
#[test]
fn custom_overrides_builtin_packet_type() {
#[derive(Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
struct OverridePayload;
const OVERRIDE_PT: u8 = 0x00;
impl<'a> Parse<'a> for OverridePayload {
type Error = crate::Error;
fn parse(_bytes: &'a [u8]) -> crate::Result<Self> {
Ok(Self)
}
}
impl<'a> crate::traits::PayloadDef<'a> for OverridePayload {
const PACKET_TYPE: u8 = OVERRIDE_PT;
const NAME: &'static str = "OVERRIDE";
}
let mut reg = PayloadRegistry::new();
reg.register::<OverridePayload>();
let bytes = [0x00, 0x00, 0x00];
let result = crate::payload::AnyPayload::dispatch_with(®, 0x00, &bytes);
let parsed = result.unwrap().unwrap();
assert!(
matches!(
parsed,
crate::payload::AnyPayload::Other {
packet_type: 0x00,
..
}
),
"expected Other override for 0x00, got {parsed:?}"
);
}
#[cfg(feature = "serde")]
#[test]
fn serde_other_round_trips_through_json() {
let mut reg = PayloadRegistry::new();
reg.register::<TestPayload>();
let bytes = [0x7Fu8];
let result = crate::payload::AnyPayload::dispatch_with(®, TEST_PACKET_TYPE, &bytes);
let parsed = result.unwrap().unwrap();
let json = serde_json::to_value(&parsed).unwrap();
let obj = json
.as_object()
.unwrap()
.get("other")
.expect("expected 'other' key");
assert_eq!(obj["packet_type"], TEST_PACKET_TYPE);
assert_eq!(obj["value"]["val"], 0x7F);
}
}