1use crate::error::{Error, Result};
9use crate::tag::{self, ApduTag};
10use crate::traits::ApduDef;
11use broadcast_common::{Parse, Serialize};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize))]
17pub struct Tune {
18 pub network_id: u16,
20 pub original_network_id: u16,
22 pub transport_stream_id: u16,
24 pub service_id: u16,
26}
27
28const TUNE_BODY: usize = 8;
30
31impl<'a> Parse<'a> for Tune {
32 type Error = Error;
33 fn parse(bytes: &'a [u8]) -> Result<Self> {
34 let body = super::parse_apdu_header(bytes, tag::TUNE, "tune")?;
35 if body.len() < TUNE_BODY {
36 return Err(Error::BufferTooShort {
37 need: TUNE_BODY,
38 have: body.len(),
39 what: "tune",
40 });
41 }
42 Ok(Self {
43 network_id: u16::from_be_bytes([body[0], body[1]]),
44 original_network_id: u16::from_be_bytes([body[2], body[3]]),
45 transport_stream_id: u16::from_be_bytes([body[4], body[5]]),
46 service_id: u16::from_be_bytes([body[6], body[7]]),
47 })
48 }
49}
50
51impl Serialize for Tune {
52 type Error = Error;
53 fn serialized_len(&self) -> usize {
54 super::apdu_len(TUNE_BODY)
55 }
56 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
57 let mut pos = super::write_apdu_header(tag::TUNE, TUNE_BODY, buf)?;
58 buf[pos..pos + 2].copy_from_slice(&self.network_id.to_be_bytes());
59 buf[pos + 2..pos + 4].copy_from_slice(&self.original_network_id.to_be_bytes());
60 buf[pos + 4..pos + 6].copy_from_slice(&self.transport_stream_id.to_be_bytes());
61 buf[pos + 6..pos + 8].copy_from_slice(&self.service_id.to_be_bytes());
62 pos += TUNE_BODY;
63 Ok(pos)
64 }
65}
66
67impl ApduDef<'_> for Tune {
68 const TAG: ApduTag = tag::TUNE;
69 const NAME: &'static str = "TUNE";
70}
71
72#[derive(Debug, Clone, Copy, PartialEq, Eq)]
75#[cfg_attr(feature = "serde", derive(serde::Serialize))]
76pub struct Replace {
77 pub replacement_ref: u8,
79 pub replaced_pid: u16,
81 pub replacement_pid: u16,
83}
84
85const REPLACE_BODY: usize = 5;
87const MAX_PID: u16 = 0x1FFF;
89
90impl<'a> Parse<'a> for Replace {
91 type Error = Error;
92 fn parse(bytes: &'a [u8]) -> Result<Self> {
93 let body = super::parse_apdu_header(bytes, tag::REPLACE, "replace")?;
94 if body.len() < REPLACE_BODY {
95 return Err(Error::BufferTooShort {
96 need: REPLACE_BODY,
97 have: body.len(),
98 what: "replace",
99 });
100 }
101 let replacement_ref = body[0];
102 let replaced_pid = (((body[1] & 0x1F) as u16) << 8) | body[2] as u16;
103 let replacement_pid = (((body[3] & 0x1F) as u16) << 8) | body[4] as u16;
104 Ok(Self {
105 replacement_ref,
106 replaced_pid,
107 replacement_pid,
108 })
109 }
110}
111
112impl Serialize for Replace {
113 type Error = Error;
114 fn serialized_len(&self) -> usize {
115 super::apdu_len(REPLACE_BODY)
116 }
117 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
118 if self.replaced_pid > MAX_PID || self.replacement_pid > MAX_PID {
119 return Err(Error::InvalidObject {
120 what: "replace",
121 reason: "PID exceeds 13-bit range (0x1FFF)",
122 });
123 }
124 let mut pos = super::write_apdu_header(tag::REPLACE, REPLACE_BODY, buf)?;
125 buf[pos] = self.replacement_ref;
126 buf[pos + 1] = 0xE0 | ((self.replaced_pid >> 8) as u8 & 0x1F);
128 buf[pos + 2] = self.replaced_pid as u8;
129 buf[pos + 3] = 0xE0 | ((self.replacement_pid >> 8) as u8 & 0x1F);
131 buf[pos + 4] = self.replacement_pid as u8;
132 pos += REPLACE_BODY;
133 Ok(pos)
134 }
135}
136
137impl ApduDef<'_> for Replace {
138 const TAG: ApduTag = tag::REPLACE;
139 const NAME: &'static str = "REPLACE";
140}
141
142#[derive(Debug, Clone, Copy, PartialEq, Eq)]
145#[cfg_attr(feature = "serde", derive(serde::Serialize))]
146pub struct ClearReplace {
147 pub replacement_ref: u8,
149}
150
151impl<'a> Parse<'a> for ClearReplace {
152 type Error = Error;
153 fn parse(bytes: &'a [u8]) -> Result<Self> {
154 let body = super::parse_apdu_header(bytes, tag::CLEAR_REPLACE, "clear_replace")?;
155 let replacement_ref = *body.first().ok_or(Error::BufferTooShort {
156 need: 1,
157 have: 0,
158 what: "clear_replace replacement_ref",
159 })?;
160 Ok(Self { replacement_ref })
161 }
162}
163
164impl Serialize for ClearReplace {
165 type Error = Error;
166 fn serialized_len(&self) -> usize {
167 super::apdu_len(1)
168 }
169 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
170 let mut pos = super::write_apdu_header(tag::CLEAR_REPLACE, 1, buf)?;
171 buf[pos] = self.replacement_ref;
172 pos += 1;
173 Ok(pos)
174 }
175}
176
177impl ApduDef<'_> for ClearReplace {
178 const TAG: ApduTag = tag::CLEAR_REPLACE;
179 const NAME: &'static str = "CLEAR_REPLACE";
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
184#[cfg_attr(feature = "serde", derive(serde::Serialize))]
185pub struct AskRelease;
186
187impl<'a> Parse<'a> for AskRelease {
188 type Error = Error;
189 fn parse(bytes: &'a [u8]) -> Result<Self> {
190 super::parse_empty_apdu(bytes, tag::ASK_RELEASE, "ask_release")?;
191 Ok(Self)
192 }
193}
194
195impl Serialize for AskRelease {
196 type Error = Error;
197 fn serialized_len(&self) -> usize {
198 super::empty_apdu_len()
199 }
200 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
201 super::serialize_empty_apdu(tag::ASK_RELEASE, buf)
202 }
203}
204
205impl ApduDef<'_> for AskRelease {
206 const TAG: ApduTag = tag::ASK_RELEASE;
207 const NAME: &'static str = "ASK_RELEASE";
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213
214 #[test]
215 fn tune_round_trips_and_bites() {
216 let t = Tune {
217 network_id: 0x1122,
218 original_network_id: 0x3344,
219 transport_stream_id: 0x5566,
220 service_id: 0x7788,
221 };
222 let bytes = t.to_bytes();
223 assert_eq!(
224 bytes,
225 [
226 0x9F, 0x84, 0x00, 0x08, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88
227 ]
228 );
229 assert_eq!(Tune::parse(&bytes).unwrap(), t);
230 let mut other = t;
231 other.service_id = 0x9999;
232 assert_ne!(bytes, other.to_bytes());
233 }
234
235 #[test]
236 fn replace_round_trips_and_bites() {
237 let r = Replace {
238 replacement_ref: 0x07,
239 replaced_pid: 0x0123, replacement_pid: 0x01FF, };
242 let bytes = r.to_bytes();
243 assert_eq!(
245 bytes,
246 [0x9F, 0x84, 0x01, 0x05, 0x07, 0xE1, 0x23, 0xE1, 0xFF]
247 );
248 let parsed = Replace::parse(&bytes).unwrap();
249 assert_eq!(parsed, r);
250 let mut other = r;
251 other.replacement_pid = 0x0001;
252 assert_ne!(bytes, other.to_bytes());
253 }
254
255 #[test]
256 fn replace_oversized_pid_rejected_not_truncated() {
257 let r = Replace {
258 replacement_ref: 0x01,
259 replaced_pid: 0x2010, replacement_pid: 0x0001,
261 };
262 let err = r.serialize_into(&mut [0u8; 32]).unwrap_err();
263 assert!(matches!(err, Error::InvalidObject { .. }));
264
265 let r2 = Replace {
266 replacement_ref: 0x01,
267 replaced_pid: 0x0001,
268 replacement_pid: 0x2010, };
270 let err2 = r2.serialize_into(&mut [0u8; 32]).unwrap_err();
271 assert!(matches!(err2, Error::InvalidObject { .. }));
272 }
273
274 #[test]
275 fn clear_replace_round_trips_and_bites() {
276 let c = ClearReplace {
277 replacement_ref: 0x42,
278 };
279 let bytes = c.to_bytes();
280 assert_eq!(bytes, [0x9F, 0x84, 0x02, 0x01, 0x42]);
281 assert_eq!(ClearReplace::parse(&bytes).unwrap(), c);
282 let other = ClearReplace {
283 replacement_ref: 0x43,
284 };
285 assert_ne!(bytes, other.to_bytes());
286 }
287
288 #[test]
289 fn ask_release_round_trips() {
290 let bytes = AskRelease.to_bytes();
291 assert_eq!(bytes, [0x9F, 0x84, 0x03, 0x00]);
292 assert_eq!(AskRelease::parse(&bytes).unwrap(), AskRelease);
293 }
294}