1use crate::error::{Error, Result};
23use crate::objects;
24use crate::tag::ApduTag;
25use alloc::vec::Vec;
26use broadcast_common::{Parse, Serialize};
27
28pub mod tag {
30 use crate::tag::ApduTag;
31 pub const STATUS_QUERY_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x00);
33 pub const TRAP_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x01);
35 pub const GET_NEXT_ITEM_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x02);
37 pub const GET_NEXT_ITEM_ACK: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x03);
39 pub const STATUS_ACK: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x04);
41}
42
43const STATUS_ITEM_LEN: usize = 4;
45
46fn read_u32(b: &[u8]) -> u32 {
47 u32::from_be_bytes([b[0], b[1], b[2], b[3]])
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52#[cfg_attr(feature = "serde", derive(serde::Serialize))]
53#[non_exhaustive]
54pub enum StatusItem {
55 SelectionInformation,
57 PortProfile,
59 ViewedService,
61 ActivationStatus,
63 Reserved(u32),
65}
66
67impl StatusItem {
68 #[must_use]
70 pub fn from_u32(v: u32) -> Self {
71 match v {
72 1 => Self::SelectionInformation,
73 2 => Self::PortProfile,
74 3 => Self::ViewedService,
75 4 => Self::ActivationStatus,
76 other => Self::Reserved(other),
77 }
78 }
79 #[must_use]
81 pub const fn to_u32(self) -> u32 {
82 match self {
83 Self::SelectionInformation => 1,
84 Self::PortProfile => 2,
85 Self::ViewedService => 3,
86 Self::ActivationStatus => 4,
87 Self::Reserved(v) => v,
88 }
89 }
90 #[must_use]
92 pub fn name(&self) -> &'static str {
93 match self {
94 Self::SelectionInformation => "Selection Information",
95 Self::PortProfile => "Port Profile",
96 Self::ViewedService => "Viewed Service",
97 Self::ActivationStatus => "Activation Status",
98 Self::Reserved(_) => "reserved",
99 }
100 }
101}
102broadcast_common::impl_spec_display!(StatusItem, Reserved);
103
104#[derive(Debug, Clone, Copy, PartialEq, Eq)]
108#[cfg_attr(feature = "serde", derive(serde::Serialize))]
109pub struct StatusQueryReq {
110 pub status_item: StatusItem,
112}
113
114#[derive(Debug, Clone, Copy, PartialEq, Eq)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize))]
117pub struct TrapReq {
118 pub status_item: StatusItem,
120}
121
122#[derive(Debug, Clone, Copy, PartialEq, Eq)]
124#[cfg_attr(feature = "serde", derive(serde::Serialize))]
125pub struct GetNextItemReq {
126 pub start_status_item: u32,
129}
130
131#[derive(Debug, Clone, Copy, PartialEq, Eq)]
133#[cfg_attr(feature = "serde", derive(serde::Serialize))]
134pub struct GetNextItemAck {
135 pub next_status_item: u32,
138}
139
140#[derive(Debug, Clone, PartialEq, Eq)]
144#[cfg_attr(feature = "serde", derive(serde::Serialize))]
145pub struct StatusAck<'a> {
146 pub status_item: StatusItem,
148 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
150 pub status_bytes: &'a [u8],
151}
152
153macro_rules! status_item_object {
154 ($ty:ident, $tag:expr, $what:literal, $field:ident, $decode:expr, $encode:expr) => {
155 impl<'a> Parse<'a> for $ty {
156 type Error = Error;
157 fn parse(bytes: &'a [u8]) -> Result<Self> {
158 let body = objects::parse_apdu_header(bytes, $tag, $what)?;
159 if body.len() < STATUS_ITEM_LEN {
160 return Err(Error::BufferTooShort {
161 need: STATUS_ITEM_LEN,
162 have: body.len(),
163 what: $what,
164 });
165 }
166 let raw = read_u32(body);
167 Ok(Self {
168 $field: $decode(raw),
169 })
170 }
171 }
172 impl Serialize for $ty {
173 type Error = Error;
174 fn serialized_len(&self) -> usize {
175 objects::apdu_len(STATUS_ITEM_LEN)
176 }
177 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
178 let pos = objects::write_apdu_header($tag, STATUS_ITEM_LEN, buf)?;
179 let raw: u32 = $encode(self.$field);
180 buf[pos..pos + STATUS_ITEM_LEN].copy_from_slice(&raw.to_be_bytes());
181 Ok(pos + STATUS_ITEM_LEN)
182 }
183 }
184 };
185}
186
187status_item_object!(
188 StatusQueryReq,
189 tag::STATUS_QUERY_REQ,
190 "StatusQueryReq",
191 status_item,
192 StatusItem::from_u32,
193 StatusItem::to_u32
194);
195status_item_object!(
196 TrapReq,
197 tag::TRAP_REQ,
198 "TrapReq",
199 status_item,
200 StatusItem::from_u32,
201 StatusItem::to_u32
202);
203status_item_object!(
204 GetNextItemReq,
205 tag::GET_NEXT_ITEM_REQ,
206 "GetNextItemReq",
207 start_status_item,
208 core::convert::identity,
209 core::convert::identity
210);
211status_item_object!(
212 GetNextItemAck,
213 tag::GET_NEXT_ITEM_ACK,
214 "GetNextItemAck",
215 next_status_item,
216 core::convert::identity,
217 core::convert::identity
218);
219
220impl<'a> Parse<'a> for StatusAck<'a> {
221 type Error = Error;
222 fn parse(bytes: &'a [u8]) -> Result<Self> {
223 let body = objects::parse_apdu_header(bytes, tag::STATUS_ACK, "StatusAck")?;
224 if body.len() < STATUS_ITEM_LEN {
225 return Err(Error::BufferTooShort {
226 need: STATUS_ITEM_LEN,
227 have: body.len(),
228 what: "StatusAck",
229 });
230 }
231 Ok(Self {
232 status_item: StatusItem::from_u32(read_u32(body)),
233 status_bytes: &body[STATUS_ITEM_LEN..],
234 })
235 }
236}
237impl Serialize for StatusAck<'_> {
238 type Error = Error;
239 fn serialized_len(&self) -> usize {
240 objects::apdu_len(STATUS_ITEM_LEN + self.status_bytes.len())
241 }
242 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
243 let body_len = STATUS_ITEM_LEN + self.status_bytes.len();
244 let mut pos = objects::write_apdu_header(tag::STATUS_ACK, body_len, buf)?;
245 buf[pos..pos + STATUS_ITEM_LEN].copy_from_slice(&self.status_item.to_u32().to_be_bytes());
246 pos += STATUS_ITEM_LEN;
247 buf[pos..pos + self.status_bytes.len()].copy_from_slice(self.status_bytes);
248 Ok(pos + self.status_bytes.len())
249 }
250}
251
252pub const SELECTION_TIME_LEN: usize = 5;
262
263#[derive(Debug, Clone, PartialEq, Eq, Default)]
265#[cfg_attr(feature = "serde", derive(serde::Serialize))]
266pub struct OutputSignal<'a> {
267 pub out_port_id: u8,
269 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
272 pub out_signal_desc: &'a [u8],
273}
274
275#[derive(Debug, Clone, PartialEq, Eq, Default)]
278#[cfg_attr(feature = "serde", derive(serde::Serialize))]
279pub struct InPortDescription<'a> {
280 pub in_port_id: u8,
282 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
285 pub in_signal_desc: &'a [u8],
286 pub outputs: Vec<OutputSignal<'a>>,
288}
289
290#[derive(Debug, Clone, PartialEq, Eq, Default)]
293#[cfg_attr(feature = "serde", derive(serde::Serialize))]
294pub struct SelectionInformation<'a> {
295 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
297 pub time: &'a [u8],
298 pub ports: Vec<InPortDescription<'a>>,
300}
301
302impl<'a> Parse<'a> for SelectionInformation<'a> {
303 type Error = Error;
304 fn parse(body: &'a [u8]) -> Result<Self> {
305 if body.len() < SELECTION_TIME_LEN {
306 return Err(Error::BufferTooShort {
307 need: SELECTION_TIME_LEN,
308 have: body.len(),
309 what: "SelectionInformation.time",
310 });
311 }
312 let time = &body[..SELECTION_TIME_LEN];
313 let mut rest = &body[SELECTION_TIME_LEN..];
314 let mut ports = Vec::new();
315 while !rest.is_empty() {
316 if rest.len() < 2 {
318 return Err(Error::BufferTooShort {
319 need: 2,
320 have: rest.len(),
321 what: "SelectionInformation.in_port",
322 });
323 }
324 let in_port_id = rest[0];
325 let in_len = rest[1] as usize;
326 let after_in = 2 + in_len;
327 if rest.len() < after_in + 2 {
328 return Err(Error::BufferTooShort {
329 need: after_in + 2,
330 have: rest.len(),
331 what: "SelectionInformation.in_signal_desc",
332 });
333 }
334 let in_signal_desc = &rest[2..after_in];
335 let length_outputs =
337 (((rest[after_in] & 0x0F) as usize) << 8) | rest[after_in + 1] as usize;
338 let outputs_start = after_in + 2;
339 let outputs_end = outputs_start + length_outputs;
340 if rest.len() < outputs_end {
341 return Err(Error::BufferTooShort {
342 need: outputs_end,
343 have: rest.len(),
344 what: "SelectionInformation.outputs",
345 });
346 }
347 let mut out_rest = &rest[outputs_start..outputs_end];
348 let mut outputs = Vec::new();
349 while !out_rest.is_empty() {
350 if out_rest.len() < 2 {
352 return Err(Error::BufferTooShort {
353 need: 2,
354 have: out_rest.len(),
355 what: "SelectionInformation.out_port",
356 });
357 }
358 let out_port_id = out_rest[0];
359 let out_len = out_rest[1] as usize;
360 let out_end = 2 + out_len;
361 if out_rest.len() < out_end {
362 return Err(Error::BufferTooShort {
363 need: out_end,
364 have: out_rest.len(),
365 what: "SelectionInformation.out_signal_desc",
366 });
367 }
368 outputs.push(OutputSignal {
369 out_port_id,
370 out_signal_desc: &out_rest[2..out_end],
371 });
372 out_rest = &out_rest[out_end..];
373 }
374 ports.push(InPortDescription {
375 in_port_id,
376 in_signal_desc,
377 outputs,
378 });
379 rest = &rest[outputs_end..];
380 }
381 Ok(Self { time, ports })
382 }
383}
384impl Serialize for SelectionInformation<'_> {
385 type Error = Error;
386 fn serialized_len(&self) -> usize {
387 let mut n = SELECTION_TIME_LEN;
388 for p in &self.ports {
389 n += 2 + p.in_signal_desc.len() + 2;
390 for o in &p.outputs {
391 n += 2 + o.out_signal_desc.len();
392 }
393 }
394 n
395 }
396 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
397 let total = self.serialized_len();
398 if buf.len() < total {
399 return Err(Error::OutputBufferTooSmall {
400 need: total,
401 have: buf.len(),
402 });
403 }
404 if self.time.len() != SELECTION_TIME_LEN {
405 return Err(Error::InvalidObject {
406 what: "SelectionInformation",
407 reason: "time must be exactly 5 bytes",
408 });
409 }
410 let mut pos = 0;
411 buf[pos..pos + SELECTION_TIME_LEN].copy_from_slice(self.time);
412 pos += SELECTION_TIME_LEN;
413 for p in &self.ports {
414 if p.in_signal_desc.len() > u8::MAX as usize {
415 return Err(Error::InvalidObject {
416 what: "SelectionInformation",
417 reason: "in_signal_desc longer than 255 bytes",
418 });
419 }
420 buf[pos] = p.in_port_id;
421 buf[pos + 1] = p.in_signal_desc.len() as u8;
422 pos += 2;
423 buf[pos..pos + p.in_signal_desc.len()].copy_from_slice(p.in_signal_desc);
424 pos += p.in_signal_desc.len();
425 let mut length_outputs = 0usize;
427 for o in &p.outputs {
428 length_outputs += 2 + o.out_signal_desc.len();
429 }
430 if length_outputs > 0x0FFF {
431 return Err(Error::InvalidObject {
432 what: "SelectionInformation",
433 reason: "length_outputs exceeds 12-bit maximum",
434 });
435 }
436 buf[pos] = ((length_outputs >> 8) & 0x0F) as u8;
438 buf[pos + 1] = (length_outputs & 0xFF) as u8;
439 pos += 2;
440 for o in &p.outputs {
441 if o.out_signal_desc.len() > u8::MAX as usize {
442 return Err(Error::InvalidObject {
443 what: "SelectionInformation",
444 reason: "out_signal_desc longer than 255 bytes",
445 });
446 }
447 buf[pos] = o.out_port_id;
448 buf[pos + 1] = o.out_signal_desc.len() as u8;
449 pos += 2;
450 buf[pos..pos + o.out_signal_desc.len()].copy_from_slice(o.out_signal_desc);
451 pos += o.out_signal_desc.len();
452 }
453 }
454 Ok(pos)
455 }
456}
457
458#[derive(Debug, Clone, PartialEq, Eq, Default)]
460#[cfg_attr(feature = "serde", derive(serde::Serialize))]
461pub struct PortDescription<'a> {
462 pub in_port_id: u8,
464 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
466 pub in_port_desc: &'a [u8],
467 pub out_port_id: u8,
469 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
471 pub out_port_desc: &'a [u8],
472}
473
474#[derive(Debug, Clone, PartialEq, Eq, Default)]
477#[cfg_attr(feature = "serde", derive(serde::Serialize))]
478pub struct PortProfile<'a> {
479 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
482 pub receiver_identification: &'a [u8],
483 pub ports: Vec<PortDescription<'a>>,
485}
486
487impl<'a> Parse<'a> for PortProfile<'a> {
488 type Error = Error;
489 fn parse(body: &'a [u8]) -> Result<Self> {
490 if body.is_empty() {
491 return Err(Error::BufferTooShort {
492 need: 1,
493 have: 0,
494 what: "PortProfile.receiver_identification_length",
495 });
496 }
497 let id_len = body[0] as usize;
498 if body.len() < 1 + id_len {
499 return Err(Error::BufferTooShort {
500 need: 1 + id_len,
501 have: body.len(),
502 what: "PortProfile.receiver_identification",
503 });
504 }
505 let receiver_identification = &body[1..1 + id_len];
506 let mut rest = &body[1 + id_len..];
507 let mut ports = Vec::new();
508 while !rest.is_empty() {
509 if rest.len() < 2 {
511 return Err(Error::BufferTooShort {
512 need: 2,
513 have: rest.len(),
514 what: "PortProfile.in_port",
515 });
516 }
517 let in_port_id = rest[0];
518 let in_len = rest[1] as usize;
519 let after_in = 2 + in_len;
520 if rest.len() < after_in + 2 {
522 return Err(Error::BufferTooShort {
523 need: after_in + 2,
524 have: rest.len(),
525 what: "PortProfile.out_port",
526 });
527 }
528 let in_port_desc = &rest[2..after_in];
529 let out_port_id = rest[after_in];
530 let out_len = rest[after_in + 1] as usize;
531 let after_out = after_in + 2 + out_len;
532 if rest.len() < after_out {
533 return Err(Error::BufferTooShort {
534 need: after_out,
535 have: rest.len(),
536 what: "PortProfile.out_port_desc",
537 });
538 }
539 ports.push(PortDescription {
540 in_port_id,
541 in_port_desc,
542 out_port_id,
543 out_port_desc: &rest[after_in + 2..after_out],
544 });
545 rest = &rest[after_out..];
546 }
547 Ok(Self {
548 receiver_identification,
549 ports,
550 })
551 }
552}
553impl Serialize for PortProfile<'_> {
554 type Error = Error;
555 fn serialized_len(&self) -> usize {
556 let mut n = 1 + self.receiver_identification.len();
557 for p in &self.ports {
558 n += 2 + p.in_port_desc.len() + 2 + p.out_port_desc.len();
559 }
560 n
561 }
562 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
563 let total = self.serialized_len();
564 if buf.len() < total {
565 return Err(Error::OutputBufferTooSmall {
566 need: total,
567 have: buf.len(),
568 });
569 }
570 if self.receiver_identification.len() > u8::MAX as usize {
571 return Err(Error::InvalidObject {
572 what: "PortProfile",
573 reason: "receiver_identification longer than 255 bytes",
574 });
575 }
576 let mut pos = 0;
577 buf[pos] = self.receiver_identification.len() as u8;
578 pos += 1;
579 buf[pos..pos + self.receiver_identification.len()]
580 .copy_from_slice(self.receiver_identification);
581 pos += self.receiver_identification.len();
582 for p in &self.ports {
583 if p.in_port_desc.len() > u8::MAX as usize || p.out_port_desc.len() > u8::MAX as usize {
584 return Err(Error::InvalidObject {
585 what: "PortProfile",
586 reason: "port description longer than 255 bytes",
587 });
588 }
589 buf[pos] = p.in_port_id;
590 buf[pos + 1] = p.in_port_desc.len() as u8;
591 pos += 2;
592 buf[pos..pos + p.in_port_desc.len()].copy_from_slice(p.in_port_desc);
593 pos += p.in_port_desc.len();
594 buf[pos] = p.out_port_id;
595 buf[pos + 1] = p.out_port_desc.len() as u8;
596 pos += 2;
597 buf[pos..pos + p.out_port_desc.len()].copy_from_slice(p.out_port_desc);
598 pos += p.out_port_desc.len();
599 }
600 Ok(pos)
601 }
602}
603
604#[derive(Debug, Clone, PartialEq, Eq, Default)]
606#[cfg_attr(feature = "serde", derive(serde::Serialize))]
607pub struct ViewedService {
608 pub service_id: u16,
610 pub component_tags: Vec<u8>,
612}
613
614impl<'a> Parse<'a> for ViewedService {
615 type Error = Error;
616 fn parse(body: &'a [u8]) -> Result<Self> {
617 if body.len() < 3 {
619 return Err(Error::BufferTooShort {
620 need: 3,
621 have: body.len(),
622 what: "ViewedService",
623 });
624 }
625 let service_id = u16::from_be_bytes([body[0], body[1]]);
626 let n = body[2] as usize;
627 if body.len() < 3 + n {
628 return Err(Error::BufferTooShort {
629 need: 3 + n,
630 have: body.len(),
631 what: "ViewedService.component_tags",
632 });
633 }
634 Ok(Self {
635 service_id,
636 component_tags: body[3..3 + n].to_vec(),
637 })
638 }
639}
640impl Serialize for ViewedService {
641 type Error = Error;
642 fn serialized_len(&self) -> usize {
643 3 + self.component_tags.len()
644 }
645 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
646 let total = self.serialized_len();
647 if buf.len() < total {
648 return Err(Error::OutputBufferTooSmall {
649 need: total,
650 have: buf.len(),
651 });
652 }
653 if self.component_tags.len() > u8::MAX as usize {
654 return Err(Error::InvalidObject {
655 what: "ViewedService",
656 reason: "more than 255 component tags",
657 });
658 }
659 buf[0..2].copy_from_slice(&self.service_id.to_be_bytes());
660 buf[2] = self.component_tags.len() as u8;
661 buf[3..3 + self.component_tags.len()].copy_from_slice(&self.component_tags);
662 Ok(total)
663 }
664}
665
666#[derive(Debug, Clone, Copy, PartialEq, Eq)]
668#[cfg_attr(feature = "serde", derive(serde::Serialize))]
669#[non_exhaustive]
670pub enum ActivationState {
671 StandbyActive,
673 On,
675 Reserved(u8),
677}
678
679impl ActivationState {
680 #[must_use]
682 pub fn from_u8(v: u8) -> Self {
683 match v {
684 1 => Self::StandbyActive,
685 2 => Self::On,
686 other => Self::Reserved(other),
687 }
688 }
689 #[must_use]
691 pub const fn to_u8(self) -> u8 {
692 match self {
693 Self::StandbyActive => 1,
694 Self::On => 2,
695 Self::Reserved(v) => v & 0x07,
696 }
697 }
698 #[must_use]
700 pub fn name(&self) -> &'static str {
701 match self {
702 Self::StandbyActive => "Standby-active",
703 Self::On => "On",
704 Self::Reserved(_) => "reserved",
705 }
706 }
707}
708broadcast_common::impl_spec_display!(ActivationState, Reserved);
709
710#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
712#[cfg_attr(feature = "serde", derive(serde::Serialize))]
713pub struct ActivationStatus {
714 pub event_activated: bool,
716 pub activation_state: Option<ActivationState>,
718}
719
720impl ActivationStatus {
721 const EVENT_ACTIVATED_BIT: u8 = 0x08;
722 const ACTIVATION_STATE_MASK: u8 = 0x07;
723}
724
725impl<'a> Parse<'a> for ActivationStatus {
726 type Error = Error;
727 fn parse(body: &'a [u8]) -> Result<Self> {
728 if body.is_empty() {
729 return Err(Error::BufferTooShort {
730 need: 1,
731 have: 0,
732 what: "ActivationStatus",
733 });
734 }
735 Ok(Self {
736 event_activated: body[0] & Self::EVENT_ACTIVATED_BIT != 0,
737 activation_state: Some(ActivationState::from_u8(
738 body[0] & Self::ACTIVATION_STATE_MASK,
739 )),
740 })
741 }
742}
743impl Serialize for ActivationStatus {
744 type Error = Error;
745 fn serialized_len(&self) -> usize {
746 1
747 }
748 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
749 if buf.is_empty() {
750 return Err(Error::OutputBufferTooSmall { need: 1, have: 0 });
751 }
752 let mut b = 0u8;
753 if self.event_activated {
754 b |= Self::EVENT_ACTIVATED_BIT;
755 }
756 if let Some(state) = self.activation_state {
757 b |= state.to_u8() & Self::ACTIVATION_STATE_MASK;
758 }
759 buf[0] = b;
760 Ok(1)
761 }
762}
763
764#[derive(Debug, Clone, PartialEq, Eq)]
766#[cfg_attr(feature = "serde", derive(serde::Serialize))]
767#[non_exhaustive]
768pub enum StatusQueryApdu<'a> {
769 StatusQueryReq(StatusQueryReq),
771 TrapReq(TrapReq),
773 GetNextItemReq(GetNextItemReq),
775 GetNextItemAck(GetNextItemAck),
777 StatusAck(StatusAck<'a>),
779}
780
781impl<'a> StatusQueryApdu<'a> {
782 pub fn parse(body: &'a [u8]) -> Result<Self> {
784 if body.len() < 3 {
785 return Err(Error::BufferTooShort {
786 need: 3,
787 have: body.len(),
788 what: "status_query apdu_tag",
789 });
790 }
791 let t = ApduTag::from_bytes(body[0], body[1], body[2]);
792 match t {
793 tag::STATUS_QUERY_REQ => Ok(Self::StatusQueryReq(StatusQueryReq::parse(body)?)),
794 tag::TRAP_REQ => Ok(Self::TrapReq(TrapReq::parse(body)?)),
795 tag::GET_NEXT_ITEM_REQ => Ok(Self::GetNextItemReq(GetNextItemReq::parse(body)?)),
796 tag::GET_NEXT_ITEM_ACK => Ok(Self::GetNextItemAck(GetNextItemAck::parse(body)?)),
797 tag::STATUS_ACK => Ok(Self::StatusAck(StatusAck::parse(body)?)),
798 _ => Err(Error::UnexpectedApduTag {
799 got: t.as_u24(),
800 expected: tag::STATUS_QUERY_REQ.as_u24(),
801 what: "status_query",
802 }),
803 }
804 }
805}
806
807impl Serialize for StatusQueryApdu<'_> {
808 type Error = Error;
809 fn serialized_len(&self) -> usize {
810 match self {
811 Self::StatusQueryReq(o) => o.serialized_len(),
812 Self::TrapReq(o) => o.serialized_len(),
813 Self::GetNextItemReq(o) => o.serialized_len(),
814 Self::GetNextItemAck(o) => o.serialized_len(),
815 Self::StatusAck(o) => o.serialized_len(),
816 }
817 }
818 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
819 match self {
820 Self::StatusQueryReq(o) => o.serialize_into(buf),
821 Self::TrapReq(o) => o.serialize_into(buf),
822 Self::GetNextItemReq(o) => o.serialize_into(buf),
823 Self::GetNextItemAck(o) => o.serialize_into(buf),
824 Self::StatusAck(o) => o.serialize_into(buf),
825 }
826 }
827}
828
829#[cfg(test)]
830mod tests {
831 use super::*;
832
833 #[test]
834 fn status_query_req_round_trips_and_bites() {
835 let req = StatusQueryReq {
836 status_item: StatusItem::PortProfile,
837 };
838 let bytes = req.to_bytes();
839 assert_eq!(bytes, [0x9F, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02]);
841 assert_eq!(StatusQueryReq::parse(&bytes).unwrap(), req);
842 let other = StatusQueryReq {
843 status_item: StatusItem::ViewedService,
844 };
845 assert_ne!(bytes, other.to_bytes());
846 }
847
848 #[test]
849 fn trap_and_getnext_round_trip() {
850 let trap = TrapReq {
851 status_item: StatusItem::ActivationStatus,
852 };
853 assert_eq!(trap.to_bytes(), [0x9F, 0x80, 0x01, 0x04, 0, 0, 0, 4]);
854 assert_eq!(TrapReq::parse(&trap.to_bytes()).unwrap(), trap);
855
856 let gnr = GetNextItemReq {
857 start_status_item: 0x0000_0007,
858 };
859 assert_eq!(gnr.to_bytes(), [0x9F, 0x80, 0x02, 0x04, 0, 0, 0, 7]);
860 assert_eq!(GetNextItemReq::parse(&gnr.to_bytes()).unwrap(), gnr);
861
862 let gna = GetNextItemAck {
863 next_status_item: 0x0000_0002,
864 };
865 assert_eq!(gna.to_bytes(), [0x9F, 0x80, 0x03, 0x04, 0, 0, 0, 2]);
866 assert_eq!(GetNextItemAck::parse(&gna.to_bytes()).unwrap(), gna);
867 }
868
869 #[test]
870 fn status_item_reserved_round_trips() {
871 let req = StatusQueryReq {
872 status_item: StatusItem::from_u32(0),
873 };
874 assert_eq!(req.status_item, StatusItem::Reserved(0));
875 assert_eq!(req.status_item.name(), "reserved");
876 assert_eq!(StatusQueryReq::parse(&req.to_bytes()).unwrap(), req);
877 }
878
879 #[test]
880 fn status_ack_with_status_bytes_round_trips_and_bites() {
881 let ack = StatusAck {
882 status_item: StatusItem::ViewedService,
883 status_bytes: &[0x01, 0x23, 0x00],
884 };
885 let bytes = ack.to_bytes();
886 assert_eq!(
888 bytes,
889 [0x9F, 0x80, 0x04, 0x07, 0, 0, 0, 3, 0x01, 0x23, 0x00]
890 );
891 assert_eq!(StatusAck::parse(&bytes).unwrap(), ack);
892 let mut other = ack.clone();
893 other.status_bytes = &[0x01, 0x23, 0x01];
894 assert_ne!(bytes, other.to_bytes());
895 }
896
897 #[test]
898 fn status_ack_empty_status_bytes_means_unsupported() {
899 let ack = StatusAck {
900 status_item: StatusItem::PortProfile,
901 status_bytes: &[],
902 };
903 let bytes = ack.to_bytes();
904 assert_eq!(bytes, [0x9F, 0x80, 0x04, 0x04, 0, 0, 0, 2]);
905 assert_eq!(StatusAck::parse(&bytes).unwrap(), ack);
906 }
907
908 #[test]
909 fn selection_information_multi_port_round_trips_and_bites() {
910 let si = SelectionInformation {
912 time: &[0x20, 0x06, 0x18, 0x12, 0x00],
913 ports: alloc::vec![
914 InPortDescription {
915 in_port_id: 0x01,
916 in_signal_desc: &[0xAA, 0xBB],
917 outputs: alloc::vec![
918 OutputSignal {
919 out_port_id: 0x00,
920 out_signal_desc: &[0x02],
921 },
922 OutputSignal {
923 out_port_id: 0x01,
924 out_signal_desc: &[0x01],
925 },
926 ],
927 },
928 InPortDescription {
929 in_port_id: 0x18,
930 in_signal_desc: &[],
931 outputs: alloc::vec![],
932 },
933 ],
934 };
935 let bytes = si.to_bytes();
936 assert_eq!(SelectionInformation::parse(&bytes).unwrap(), si);
937 let mut other = si.clone();
939 other.ports[0].outputs[1].out_port_id = 0x02;
940 assert_ne!(bytes, other.to_bytes());
941 assert_eq!(
945 bytes,
946 [
947 0x20, 0x06, 0x18, 0x12, 0x00, 0x01, 0x02, 0xAA, 0xBB, 0x00, 0x06, 0x00, 0x01, 0x02, 0x01, 0x01, 0x01, 0x18, 0x00, 0x00, 0x00, ]
955 );
956 }
957
958 #[test]
959 fn port_profile_multi_port_round_trips_and_bites() {
960 let pp = PortProfile {
961 receiver_identification: b"ACME-TV1",
962 ports: alloc::vec![
963 PortDescription {
964 in_port_id: 0x00,
965 in_port_desc: b"RF0",
966 out_port_id: 0x00,
967 out_port_desc: b"D0",
968 },
969 PortDescription {
970 in_port_id: 0x18,
971 in_port_desc: b"CI",
972 out_port_id: 0x10,
973 out_port_desc: b"SCART",
974 },
975 ],
976 };
977 let bytes = pp.to_bytes();
978 assert_eq!(PortProfile::parse(&bytes).unwrap(), pp);
979 let mut other = pp.clone();
980 other.ports[1].out_port_id = 0x11;
981 assert_ne!(bytes, other.to_bytes());
982 }
983
984 #[test]
985 fn viewed_service_multi_component_round_trips_and_bites() {
986 let vs = ViewedService {
987 service_id: 0x1234,
988 component_tags: alloc::vec![0x10, 0x20, 0x30],
989 };
990 let bytes = vs.to_bytes();
991 assert_eq!(bytes, [0x12, 0x34, 0x03, 0x10, 0x20, 0x30]);
992 assert_eq!(ViewedService::parse(&bytes).unwrap(), vs);
993 let mut other = vs.clone();
994 other.component_tags[2] = 0x31;
995 assert_ne!(bytes, other.to_bytes());
996 }
997
998 #[test]
999 fn activation_status_packs_bits() {
1000 let a = ActivationStatus {
1001 event_activated: true,
1002 activation_state: Some(ActivationState::On),
1003 };
1004 let bytes = a.to_bytes();
1005 assert_eq!(bytes, [0x0A]);
1007 assert_eq!(ActivationStatus::parse(&bytes).unwrap(), a);
1008 let b = ActivationStatus {
1010 event_activated: false,
1011 activation_state: Some(ActivationState::StandbyActive),
1012 };
1013 assert_eq!(b.to_bytes(), [0x01]);
1014 assert_eq!(ActivationStatus::parse(&[0x01]).unwrap(), b);
1015 }
1016
1017 #[test]
1018 fn dispatch_routes_each_tag() {
1019 let q = StatusQueryReq {
1020 status_item: StatusItem::SelectionInformation,
1021 }
1022 .to_bytes();
1023 assert!(matches!(
1024 StatusQueryApdu::parse(&q).unwrap(),
1025 StatusQueryApdu::StatusQueryReq(_)
1026 ));
1027 let ack = StatusAck {
1028 status_item: StatusItem::ActivationStatus,
1029 status_bytes: &[0x0A],
1030 }
1031 .to_bytes();
1032 let parsed = StatusQueryApdu::parse(&ack).unwrap();
1033 assert!(matches!(parsed, StatusQueryApdu::StatusAck(_)));
1034 assert_eq!(parsed.to_bytes(), ack);
1035 }
1036}