1use super::descriptor_body;
10use crate::error::{Error, Result};
11use dvb_common::{Parse, Serialize};
12
13pub const TAG: u8 = 0x7E;
15pub const HEADER_LEN: usize = 2;
17pub const BODY_LEN: usize = 1;
19
20const USER_DEFINED_MASK: u8 = 0b1000_0000;
21const RESERVED_MASK: u8 = 0b0111_0000;
22const DO_NOT_SCRAMBLE_MASK: u8 = 0b0000_1000;
23const CONTROL_REMOTE_ACCESS_MASK: u8 = 0b0000_0110;
24const CONTROL_REMOTE_ACCESS_SHIFT: u8 = 1;
25const DO_NOT_APPLY_REVOCATION_MASK: u8 = 0b0000_0001;
26pub const CONTROL_REMOTE_ACCESS_MAX: u8 = 0b11;
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
31#[cfg_attr(feature = "serde", derive(serde::Serialize))]
32#[non_exhaustive]
33pub enum ControlRemoteAccess {
34 Enabled,
36 EnabledManagedDomain,
39 EnabledManagedDomainTimeLimited,
42 NotAllowed,
44 Reserved(u8),
46}
47
48impl ControlRemoteAccess {
49 #[must_use]
50 pub fn from_u8(v: u8) -> Self {
53 match v {
54 0b00 => Self::Enabled,
55 0b01 => Self::EnabledManagedDomain,
56 0b10 => Self::EnabledManagedDomainTimeLimited,
57 0b11 => Self::NotAllowed,
58 v => Self::Reserved(v),
59 }
60 }
61
62 #[must_use]
63 pub fn to_u8(self) -> u8 {
65 match self {
66 Self::Enabled => 0b00,
67 Self::EnabledManagedDomain => 0b01,
68 Self::EnabledManagedDomainTimeLimited => 0b10,
69 Self::NotAllowed => 0b11,
70 Self::Reserved(v) => v,
71 }
72 }
73
74 #[must_use]
75 pub fn name(self) -> &'static str {
77 match self {
78 Self::Enabled => "redistribution enabled",
79 Self::EnabledManagedDomain => "redistribution enabled (managed domain only)",
80 Self::EnabledManagedDomainTimeLimited => {
81 "redistribution enabled (managed domain, time-limited)"
82 }
83 Self::NotAllowed => "redistribution not allowed",
84 Self::Reserved(_) => "reserved",
85 }
86 }
87}
88
89#[derive(Debug, Clone, Copy, PartialEq, Eq)]
91#[cfg_attr(feature = "serde", derive(serde::Serialize))]
92pub struct FtaContentManagementDescriptor {
93 pub user_defined: bool,
95 pub do_not_scramble: bool,
97 pub control_remote_access_over_internet: ControlRemoteAccess,
99 pub do_not_apply_revocation: bool,
101}
102
103impl<'a> Parse<'a> for FtaContentManagementDescriptor {
104 type Error = crate::error::Error;
105 fn parse(bytes: &'a [u8]) -> Result<Self> {
106 let body = descriptor_body(
107 bytes,
108 TAG,
109 "FtaContentManagementDescriptor",
110 "unexpected tag for FTA_content_management_descriptor",
111 )?;
112 if body.len() != BODY_LEN {
113 return Err(Error::InvalidDescriptor {
114 tag: TAG,
115 reason: "FTA_content_management_descriptor length must be exactly 1",
116 });
117 }
118 let flags = body[0];
119 Ok(Self {
121 user_defined: flags & USER_DEFINED_MASK != 0,
122 do_not_scramble: flags & DO_NOT_SCRAMBLE_MASK != 0,
123 control_remote_access_over_internet: ControlRemoteAccess::from_u8(
124 (flags & CONTROL_REMOTE_ACCESS_MASK) >> CONTROL_REMOTE_ACCESS_SHIFT,
125 ),
126 do_not_apply_revocation: flags & DO_NOT_APPLY_REVOCATION_MASK != 0,
127 })
128 }
129}
130
131impl Serialize for FtaContentManagementDescriptor {
132 type Error = crate::error::Error;
133 fn serialized_len(&self) -> usize {
134 HEADER_LEN + BODY_LEN
135 }
136
137 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
138 if self.control_remote_access_over_internet.to_u8() > CONTROL_REMOTE_ACCESS_MAX {
139 return Err(Error::InvalidDescriptor {
140 tag: TAG,
141 reason: "control_remote_access_over_internet exceeds 2 bits",
142 });
143 }
144 let len = self.serialized_len();
145 if buf.len() < len {
146 return Err(Error::OutputBufferTooSmall {
147 need: len,
148 have: buf.len(),
149 });
150 }
151 let mut flags = RESERVED_MASK;
153 if self.user_defined {
154 flags |= USER_DEFINED_MASK;
155 }
156 if self.do_not_scramble {
157 flags |= DO_NOT_SCRAMBLE_MASK;
158 }
159 flags |= (self.control_remote_access_over_internet.to_u8() << CONTROL_REMOTE_ACCESS_SHIFT)
160 & CONTROL_REMOTE_ACCESS_MASK;
161 if self.do_not_apply_revocation {
162 flags |= DO_NOT_APPLY_REVOCATION_MASK;
163 }
164 buf[0] = TAG;
165 buf[1] = BODY_LEN as u8;
166 buf[HEADER_LEN] = flags;
167 Ok(len)
168 }
169}
170impl<'a> crate::traits::DescriptorDef<'a> for FtaContentManagementDescriptor {
171 const TAG: u8 = TAG;
172 const NAME: &'static str = "FTA_CONTENT_MANAGEMENT";
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178
179 #[test]
180 fn parse_extracts_all_fields() {
181 let bytes = [TAG, 1, 0x8D];
184 let d = FtaContentManagementDescriptor::parse(&bytes).unwrap();
185 assert!(d.user_defined);
186 assert!(d.do_not_scramble);
187 assert_eq!(
188 d.control_remote_access_over_internet,
189 ControlRemoteAccess::EnabledManagedDomainTimeLimited
190 );
191 assert!(d.do_not_apply_revocation);
192 }
193
194 #[test]
195 fn parse_ignores_reserved_bits() {
196 let bytes = [TAG, 1, 0x70];
198 let d = FtaContentManagementDescriptor::parse(&bytes).unwrap();
199 assert!(!d.user_defined);
200 assert!(!d.do_not_scramble);
201 assert_eq!(
202 d.control_remote_access_over_internet,
203 ControlRemoteAccess::Enabled
204 );
205 assert!(!d.do_not_apply_revocation);
206 }
207
208 #[test]
209 fn parse_rejects_wrong_tag() {
210 assert!(matches!(
211 FtaContentManagementDescriptor::parse(&[0x7F, 1, 0]).unwrap_err(),
212 Error::InvalidDescriptor { tag: 0x7F, .. }
213 ));
214 }
215
216 #[test]
217 fn parse_rejects_wrong_length() {
218 assert!(matches!(
219 FtaContentManagementDescriptor::parse(&[TAG, 2, 0, 0]).unwrap_err(),
220 Error::InvalidDescriptor { tag: TAG, .. }
221 ));
222 }
223
224 #[test]
225 fn parse_rejects_short_body() {
226 assert!(matches!(
227 FtaContentManagementDescriptor::parse(&[TAG, 1]).unwrap_err(),
228 Error::BufferTooShort { .. }
229 ));
230 }
231
232 #[test]
233 fn serialize_round_trip() {
234 let d = FtaContentManagementDescriptor {
235 user_defined: true,
236 do_not_scramble: true,
237 control_remote_access_over_internet:
238 ControlRemoteAccess::EnabledManagedDomainTimeLimited,
239 do_not_apply_revocation: true,
240 };
241 let mut buf = vec![0u8; d.serialized_len()];
242 d.serialize_into(&mut buf).unwrap();
243 assert_eq!(buf, [TAG, 1, 0xFD]);
245 assert_eq!(FtaContentManagementDescriptor::parse(&buf).unwrap(), d);
246 }
247
248 #[test]
249 fn serialize_rejects_too_small_buffer() {
250 let d = FtaContentManagementDescriptor {
251 user_defined: false,
252 do_not_scramble: false,
253 control_remote_access_over_internet: ControlRemoteAccess::Enabled,
254 do_not_apply_revocation: false,
255 };
256 let mut buf = vec![0u8; 2];
257 assert!(matches!(
258 d.serialize_into(&mut buf).unwrap_err(),
259 Error::OutputBufferTooSmall { .. }
260 ));
261 }
262
263 #[test]
264 fn serialize_rejects_over_range_cra() {
265 let d = FtaContentManagementDescriptor {
266 user_defined: false,
267 do_not_scramble: false,
268 control_remote_access_over_internet: ControlRemoteAccess::Reserved(0b100), do_not_apply_revocation: false,
270 };
271 let mut buf = vec![0u8; d.serialized_len()];
272 assert!(matches!(
273 d.serialize_into(&mut buf).unwrap_err(),
274 Error::InvalidDescriptor { tag: TAG, .. }
275 ));
276 }
277
278 #[cfg(feature = "serde")]
279 #[test]
280 fn serde_round_trip() {
281 let d = FtaContentManagementDescriptor {
282 user_defined: true,
283 do_not_scramble: false,
284 control_remote_access_over_internet: ControlRemoteAccess::EnabledManagedDomain,
285 do_not_apply_revocation: true,
286 };
287 let json = serde_json::to_string(&d).unwrap();
288 let _v: serde_json::Value = serde_json::from_str(&json).unwrap();
290 }
291
292 #[test]
293 fn control_remote_access_full_range_round_trip() {
294 for b in 0..=0xFF_u8 {
295 let cra = ControlRemoteAccess::from_u8(b);
296 assert_eq!(cra.to_u8(), b, "round-trip failed for byte 0x{b:02X}");
297 }
298 }
299}