Skip to main content

dvb_si/descriptors/
time_shifted_service.rs

1//! Time Shifted Service Descriptor — ETSI EN 300 468 §6.2.45 (tag 0x4C).
2//!
3//! Table 104 (PDF p. 109). Carried in the SDT for an NVOD time-shifted
4//! service; points at the reference service whose schedule it shifts. Body is
5//! a single 16-bit `reference_service_id`.
6
7use super::descriptor_body;
8use crate::error::{Error, Result};
9use dvb_common::{Parse, Serialize};
10
11/// Descriptor tag for time_shifted_service_descriptor.
12pub const TAG: u8 = 0x4C;
13const HEADER_LEN: usize = 2;
14const BODY_LEN: usize = 2;
15
16/// Time Shifted Service Descriptor.
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18#[cfg_attr(feature = "serde", derive(serde::Serialize))]
19pub struct TimeShiftedServiceDescriptor {
20    /// service_id of the reference (NVOD reference) service.
21    pub reference_service_id: u16,
22}
23
24impl<'a> Parse<'a> for TimeShiftedServiceDescriptor {
25    type Error = crate::error::Error;
26    fn parse(bytes: &'a [u8]) -> Result<Self> {
27        let body = descriptor_body(
28            bytes,
29            TAG,
30            "TimeShiftedServiceDescriptor",
31            "unexpected tag for time_shifted_service_descriptor",
32        )?;
33        if body.len() != BODY_LEN {
34            return Err(Error::InvalidDescriptor {
35                tag: TAG,
36                reason: "time_shifted_service_descriptor length must be 2",
37            });
38        }
39        Ok(Self {
40            reference_service_id: u16::from_be_bytes([body[0], body[1]]),
41        })
42    }
43}
44
45impl Serialize for TimeShiftedServiceDescriptor {
46    type Error = crate::error::Error;
47    fn serialized_len(&self) -> usize {
48        HEADER_LEN + BODY_LEN
49    }
50
51    fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
52        let len = self.serialized_len();
53        if buf.len() < len {
54            return Err(Error::OutputBufferTooSmall {
55                need: len,
56                have: buf.len(),
57            });
58        }
59        buf[0] = TAG;
60        buf[1] = BODY_LEN as u8;
61        buf[2..4].copy_from_slice(&self.reference_service_id.to_be_bytes());
62        Ok(len)
63    }
64}
65impl<'a> crate::traits::DescriptorDef<'a> for TimeShiftedServiceDescriptor {
66    const TAG: u8 = TAG;
67    const NAME: &'static str = "TIME_SHIFTED_SERVICE";
68}
69
70#[cfg(test)]
71mod tests {
72    use super::*;
73
74    #[test]
75    fn parse_reference_service_id() {
76        let bytes = [TAG, 2, 0x12, 0x34];
77        let d = TimeShiftedServiceDescriptor::parse(&bytes).unwrap();
78        assert_eq!(d.reference_service_id, 0x1234);
79    }
80
81    #[test]
82    fn parse_rejects_wrong_tag() {
83        assert!(matches!(
84            TimeShiftedServiceDescriptor::parse(&[0x4D, 2, 0x12, 0x34]).unwrap_err(),
85            Error::InvalidDescriptor { tag: 0x4D, .. }
86        ));
87    }
88
89    #[test]
90    fn parse_rejects_short_buffer() {
91        // declares length 2 but only 1 body byte present
92        let bytes = [TAG, 2, 0x12];
93        assert!(matches!(
94            TimeShiftedServiceDescriptor::parse(&bytes).unwrap_err(),
95            Error::BufferTooShort { .. }
96        ));
97    }
98
99    #[test]
100    fn parse_rejects_wrong_length() {
101        let bytes = [TAG, 3, 0x12, 0x34, 0x00];
102        assert!(matches!(
103            TimeShiftedServiceDescriptor::parse(&bytes).unwrap_err(),
104            Error::InvalidDescriptor { tag: TAG, .. }
105        ));
106    }
107
108    #[test]
109    fn serialize_round_trip() {
110        let d = TimeShiftedServiceDescriptor {
111            reference_service_id: 0xABCD,
112        };
113        let mut buf = vec![0u8; d.serialized_len()];
114        d.serialize_into(&mut buf).unwrap();
115        assert_eq!(TimeShiftedServiceDescriptor::parse(&buf).unwrap(), d);
116    }
117
118    #[test]
119    fn serialize_rejects_small_buffer() {
120        let d = TimeShiftedServiceDescriptor {
121            reference_service_id: 1,
122        };
123        let mut tiny = [0u8; 3];
124        assert!(matches!(
125            d.serialize_into(&mut tiny).unwrap_err(),
126            Error::OutputBufferTooSmall { .. }
127        ));
128    }
129
130    #[cfg(feature = "serde")]
131    #[test]
132    fn serde_round_trip() {
133        let d = TimeShiftedServiceDescriptor {
134            reference_service_id: 0x1234,
135        };
136        let json = serde_json::to_string(&d).unwrap();
137        // Serialize-only: assert the emitted JSON re-parses (serialize-stable).
138        let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
139    }
140}