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;
87
88impl<'a> Parse<'a> for Replace {
89 type Error = Error;
90 fn parse(bytes: &'a [u8]) -> Result<Self> {
91 let body = super::parse_apdu_header(bytes, tag::REPLACE, "replace")?;
92 if body.len() < REPLACE_BODY {
93 return Err(Error::BufferTooShort {
94 need: REPLACE_BODY,
95 have: body.len(),
96 what: "replace",
97 });
98 }
99 let replacement_ref = body[0];
100 let replaced_pid = (((body[1] & 0x1F) as u16) << 8) | body[2] as u16;
101 let replacement_pid = (((body[3] & 0x1F) as u16) << 8) | body[4] as u16;
102 Ok(Self {
103 replacement_ref,
104 replaced_pid,
105 replacement_pid,
106 })
107 }
108}
109
110impl Serialize for Replace {
111 type Error = Error;
112 fn serialized_len(&self) -> usize {
113 super::apdu_len(REPLACE_BODY)
114 }
115 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
116 let mut pos = super::write_apdu_header(tag::REPLACE, REPLACE_BODY, buf)?;
117 buf[pos] = self.replacement_ref;
118 buf[pos + 1] = 0xE0 | ((self.replaced_pid >> 8) as u8 & 0x1F);
120 buf[pos + 2] = self.replaced_pid as u8;
121 buf[pos + 3] = 0xE0 | ((self.replacement_pid >> 8) as u8 & 0x1F);
123 buf[pos + 4] = self.replacement_pid as u8;
124 pos += REPLACE_BODY;
125 Ok(pos)
126 }
127}
128
129impl ApduDef<'_> for Replace {
130 const TAG: ApduTag = tag::REPLACE;
131 const NAME: &'static str = "REPLACE";
132}
133
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
137#[cfg_attr(feature = "serde", derive(serde::Serialize))]
138pub struct ClearReplace {
139 pub replacement_ref: u8,
141}
142
143impl<'a> Parse<'a> for ClearReplace {
144 type Error = Error;
145 fn parse(bytes: &'a [u8]) -> Result<Self> {
146 let body = super::parse_apdu_header(bytes, tag::CLEAR_REPLACE, "clear_replace")?;
147 let replacement_ref = *body.first().ok_or(Error::BufferTooShort {
148 need: 1,
149 have: 0,
150 what: "clear_replace replacement_ref",
151 })?;
152 Ok(Self { replacement_ref })
153 }
154}
155
156impl Serialize for ClearReplace {
157 type Error = Error;
158 fn serialized_len(&self) -> usize {
159 super::apdu_len(1)
160 }
161 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
162 let mut pos = super::write_apdu_header(tag::CLEAR_REPLACE, 1, buf)?;
163 buf[pos] = self.replacement_ref;
164 pos += 1;
165 Ok(pos)
166 }
167}
168
169impl ApduDef<'_> for ClearReplace {
170 const TAG: ApduTag = tag::CLEAR_REPLACE;
171 const NAME: &'static str = "CLEAR_REPLACE";
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
176#[cfg_attr(feature = "serde", derive(serde::Serialize))]
177pub struct AskRelease;
178
179impl<'a> Parse<'a> for AskRelease {
180 type Error = Error;
181 fn parse(bytes: &'a [u8]) -> Result<Self> {
182 super::parse_empty_apdu(bytes, tag::ASK_RELEASE, "ask_release")?;
183 Ok(Self)
184 }
185}
186
187impl Serialize for AskRelease {
188 type Error = Error;
189 fn serialized_len(&self) -> usize {
190 super::empty_apdu_len()
191 }
192 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
193 super::serialize_empty_apdu(tag::ASK_RELEASE, buf)
194 }
195}
196
197impl ApduDef<'_> for AskRelease {
198 const TAG: ApduTag = tag::ASK_RELEASE;
199 const NAME: &'static str = "ASK_RELEASE";
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205
206 #[test]
207 fn tune_round_trips_and_bites() {
208 let t = Tune {
209 network_id: 0x1122,
210 original_network_id: 0x3344,
211 transport_stream_id: 0x5566,
212 service_id: 0x7788,
213 };
214 let bytes = t.to_bytes();
215 assert_eq!(
216 bytes,
217 [
218 0x9F, 0x84, 0x00, 0x08, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88
219 ]
220 );
221 assert_eq!(Tune::parse(&bytes).unwrap(), t);
222 let mut other = t;
223 other.service_id = 0x9999;
224 assert_ne!(bytes, other.to_bytes());
225 }
226
227 #[test]
228 fn replace_round_trips_and_bites() {
229 let r = Replace {
230 replacement_ref: 0x07,
231 replaced_pid: 0x0123, replacement_pid: 0x01FF, };
234 let bytes = r.to_bytes();
235 assert_eq!(
237 bytes,
238 [0x9F, 0x84, 0x01, 0x05, 0x07, 0xE1, 0x23, 0xE1, 0xFF]
239 );
240 let parsed = Replace::parse(&bytes).unwrap();
241 assert_eq!(parsed, r);
242 let mut other = r;
243 other.replacement_pid = 0x0001;
244 assert_ne!(bytes, other.to_bytes());
245 }
246
247 #[test]
248 fn clear_replace_round_trips_and_bites() {
249 let c = ClearReplace {
250 replacement_ref: 0x42,
251 };
252 let bytes = c.to_bytes();
253 assert_eq!(bytes, [0x9F, 0x84, 0x02, 0x01, 0x42]);
254 assert_eq!(ClearReplace::parse(&bytes).unwrap(), c);
255 let other = ClearReplace {
256 replacement_ref: 0x43,
257 };
258 assert_ne!(bytes, other.to_bytes());
259 }
260
261 #[test]
262 fn ask_release_round_trips() {
263 let bytes = AskRelease.to_bytes();
264 assert_eq!(bytes, [0x9F, 0x84, 0x03, 0x00]);
265 assert_eq!(AskRelease::parse(&bytes).unwrap(), AskRelease);
266 }
267}