1use crate::error::{Error, Result};
23use crate::objects;
24use alloc::vec::Vec;
25use broadcast_common::{Parse, Serialize};
26
27pub mod tag {
29 use crate::tag::ApduTag;
30 pub const STATUS_QUERY_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x00);
32 pub const TRAP_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x01);
34 pub const GET_NEXT_ITEM_REQ: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x02);
36 pub const GET_NEXT_ITEM_ACK: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x03);
38 pub const STATUS_ACK: ApduTag = ApduTag::from_bytes(0x9F, 0x80, 0x04);
40}
41
42const STATUS_ITEM_LEN: usize = 4;
44
45fn read_u32(b: &[u8]) -> u32 {
46 u32::from_be_bytes([b[0], b[1], b[2], b[3]])
47}
48
49#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize))]
52#[non_exhaustive]
53pub enum StatusItem {
54 SelectionInformation,
56 PortProfile,
58 ViewedService,
60 ActivationStatus,
62 Reserved(u32),
64}
65
66impl StatusItem {
67 #[must_use]
69 pub fn from_u32(v: u32) -> Self {
70 match v {
71 1 => Self::SelectionInformation,
72 2 => Self::PortProfile,
73 3 => Self::ViewedService,
74 4 => Self::ActivationStatus,
75 other => Self::Reserved(other),
76 }
77 }
78 #[must_use]
80 pub const fn to_u32(self) -> u32 {
81 match self {
82 Self::SelectionInformation => 1,
83 Self::PortProfile => 2,
84 Self::ViewedService => 3,
85 Self::ActivationStatus => 4,
86 Self::Reserved(v) => v,
87 }
88 }
89 #[must_use]
91 pub fn name(&self) -> &'static str {
92 match self {
93 Self::SelectionInformation => "Selection Information",
94 Self::PortProfile => "Port Profile",
95 Self::ViewedService => "Viewed Service",
96 Self::ActivationStatus => "Activation Status",
97 Self::Reserved(_) => "reserved",
98 }
99 }
100}
101broadcast_common::impl_spec_display!(StatusItem, Reserved);
102
103#[derive(Debug, Clone, Copy, PartialEq, Eq)]
107#[cfg_attr(feature = "serde", derive(serde::Serialize))]
108pub struct StatusQueryReq {
109 #[cfg_attr(feature = "serde", serde(skip))]
113 pub length_form: crate::length::LengthForm,
114
115 pub status_item: StatusItem,
117}
118
119#[derive(Debug, Clone, Copy, PartialEq, Eq)]
121#[cfg_attr(feature = "serde", derive(serde::Serialize))]
122pub struct TrapReq {
123 #[cfg_attr(feature = "serde", serde(skip))]
127 pub length_form: crate::length::LengthForm,
128
129 pub status_item: StatusItem,
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize))]
136pub struct GetNextItemReq {
137 #[cfg_attr(feature = "serde", serde(skip))]
141 pub length_form: crate::length::LengthForm,
142
143 pub start_status_item: u32,
146}
147
148#[derive(Debug, Clone, Copy, PartialEq, Eq)]
150#[cfg_attr(feature = "serde", derive(serde::Serialize))]
151pub struct GetNextItemAck {
152 #[cfg_attr(feature = "serde", serde(skip))]
156 pub length_form: crate::length::LengthForm,
157
158 pub next_status_item: u32,
161}
162
163#[derive(Debug, Clone, PartialEq, Eq)]
167#[cfg_attr(feature = "serde", derive(serde::Serialize))]
168pub struct StatusAck<'a> {
169 #[cfg_attr(feature = "serde", serde(skip))]
173 pub length_form: crate::length::LengthForm,
174
175 pub status_item: StatusItem,
177 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
179 pub status_bytes: &'a [u8],
180}
181
182macro_rules! status_item_object {
183 ($ty:ident, $tag:expr, $what:literal, $field:ident, $decode:expr, $encode:expr) => {
184 impl<'a> Parse<'a> for $ty {
185 type Error = Error;
186 fn parse(bytes: &'a [u8]) -> Result<Self> {
187 let (length_form, body) = objects::parse_apdu_header(bytes, $tag, $what)?;
188 if body.len() < STATUS_ITEM_LEN {
189 return Err(Error::BufferTooShort {
190 need: STATUS_ITEM_LEN,
191 have: body.len(),
192 what: $what,
193 });
194 }
195 objects::reject_trailing_body(body, STATUS_ITEM_LEN, $what)?;
196 let raw = read_u32(body);
197 Ok(Self {
198 length_form,
199 $field: $decode(raw),
200 })
201 }
202 }
203 impl Serialize for $ty {
204 type Error = Error;
205 fn serialized_len(&self) -> usize {
206 objects::apdu_len(STATUS_ITEM_LEN, self.length_form)
207 }
208 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
209 let pos = objects::write_apdu_header($tag, STATUS_ITEM_LEN, self.length_form, buf)?;
210 let raw: u32 = $encode(self.$field);
211 buf[pos..pos + STATUS_ITEM_LEN].copy_from_slice(&raw.to_be_bytes());
212 Ok(pos + STATUS_ITEM_LEN)
213 }
214 }
215 };
216}
217
218status_item_object!(
219 StatusQueryReq,
220 tag::STATUS_QUERY_REQ,
221 "StatusQueryReq",
222 status_item,
223 StatusItem::from_u32,
224 StatusItem::to_u32
225);
226status_item_object!(
227 TrapReq,
228 tag::TRAP_REQ,
229 "TrapReq",
230 status_item,
231 StatusItem::from_u32,
232 StatusItem::to_u32
233);
234status_item_object!(
235 GetNextItemReq,
236 tag::GET_NEXT_ITEM_REQ,
237 "GetNextItemReq",
238 start_status_item,
239 core::convert::identity,
240 core::convert::identity
241);
242status_item_object!(
243 GetNextItemAck,
244 tag::GET_NEXT_ITEM_ACK,
245 "GetNextItemAck",
246 next_status_item,
247 core::convert::identity,
248 core::convert::identity
249);
250
251impl<'a> Parse<'a> for StatusAck<'a> {
252 type Error = Error;
253 fn parse(bytes: &'a [u8]) -> Result<Self> {
254 let (length_form, body) = objects::parse_apdu_header(bytes, tag::STATUS_ACK, "StatusAck")?;
255 if body.len() < STATUS_ITEM_LEN {
256 return Err(Error::BufferTooShort {
257 need: STATUS_ITEM_LEN,
258 have: body.len(),
259 what: "StatusAck",
260 });
261 }
262 Ok(Self {
263 length_form,
264 status_item: StatusItem::from_u32(read_u32(body)),
265 status_bytes: &body[STATUS_ITEM_LEN..],
266 })
267 }
268}
269impl Serialize for StatusAck<'_> {
270 type Error = Error;
271 fn serialized_len(&self) -> usize {
272 objects::apdu_len(STATUS_ITEM_LEN + self.status_bytes.len(), self.length_form)
273 }
274 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
275 let body_len = STATUS_ITEM_LEN + self.status_bytes.len();
276 let mut pos = objects::write_apdu_header(tag::STATUS_ACK, body_len, self.length_form, buf)?;
277 buf[pos..pos + STATUS_ITEM_LEN].copy_from_slice(&self.status_item.to_u32().to_be_bytes());
278 pos += STATUS_ITEM_LEN;
279 buf[pos..pos + self.status_bytes.len()].copy_from_slice(self.status_bytes);
280 Ok(pos + self.status_bytes.len())
281 }
282}
283
284pub const SELECTION_TIME_LEN: usize = 5;
294
295#[derive(Debug, Clone, PartialEq, Eq, Default)]
297#[cfg_attr(feature = "serde", derive(serde::Serialize))]
298pub struct OutputSignal<'a> {
299 pub out_port_id: u8,
301 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
304 pub out_signal_desc: &'a [u8],
305}
306
307#[derive(Debug, Clone, PartialEq, Eq, Default)]
310#[cfg_attr(feature = "serde", derive(serde::Serialize))]
311pub struct InPortDescription<'a> {
312 pub in_port_id: u8,
314 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
317 pub in_signal_desc: &'a [u8],
318 pub outputs: Vec<OutputSignal<'a>>,
320}
321
322#[derive(Debug, Clone, PartialEq, Eq, Default)]
325#[cfg_attr(feature = "serde", derive(serde::Serialize))]
326pub struct SelectionInformation<'a> {
327 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
329 pub time: &'a [u8],
330 pub ports: Vec<InPortDescription<'a>>,
332}
333
334impl<'a> Parse<'a> for SelectionInformation<'a> {
335 type Error = Error;
336 fn parse(body: &'a [u8]) -> Result<Self> {
337 if body.len() < SELECTION_TIME_LEN {
338 return Err(Error::BufferTooShort {
339 need: SELECTION_TIME_LEN,
340 have: body.len(),
341 what: "SelectionInformation.time",
342 });
343 }
344 let time = &body[..SELECTION_TIME_LEN];
345 let mut rest = &body[SELECTION_TIME_LEN..];
346 let mut ports = Vec::new();
347 while !rest.is_empty() {
348 if rest.len() < 2 {
350 return Err(Error::BufferTooShort {
351 need: 2,
352 have: rest.len(),
353 what: "SelectionInformation.in_port",
354 });
355 }
356 let in_port_id = rest[0];
357 let in_len = rest[1] as usize;
358 let after_in = 2 + in_len;
359 if rest.len() < after_in + 2 {
360 return Err(Error::BufferTooShort {
361 need: after_in + 2,
362 have: rest.len(),
363 what: "SelectionInformation.in_signal_desc",
364 });
365 }
366 let in_signal_desc = &rest[2..after_in];
367 let length_outputs =
369 (((rest[after_in] & 0x0F) as usize) << 8) | rest[after_in + 1] as usize;
370 let outputs_start = after_in + 2;
371 let outputs_end = outputs_start + length_outputs;
372 if rest.len() < outputs_end {
373 return Err(Error::BufferTooShort {
374 need: outputs_end,
375 have: rest.len(),
376 what: "SelectionInformation.outputs",
377 });
378 }
379 let mut out_rest = &rest[outputs_start..outputs_end];
380 let mut outputs = Vec::new();
381 while !out_rest.is_empty() {
382 if out_rest.len() < 2 {
384 return Err(Error::BufferTooShort {
385 need: 2,
386 have: out_rest.len(),
387 what: "SelectionInformation.out_port",
388 });
389 }
390 let out_port_id = out_rest[0];
391 let out_len = out_rest[1] as usize;
392 let out_end = 2 + out_len;
393 if out_rest.len() < out_end {
394 return Err(Error::BufferTooShort {
395 need: out_end,
396 have: out_rest.len(),
397 what: "SelectionInformation.out_signal_desc",
398 });
399 }
400 outputs.push(OutputSignal {
401 out_port_id,
402 out_signal_desc: &out_rest[2..out_end],
403 });
404 out_rest = &out_rest[out_end..];
405 }
406 ports.push(InPortDescription {
407 in_port_id,
408 in_signal_desc,
409 outputs,
410 });
411 rest = &rest[outputs_end..];
412 }
413 Ok(Self { time, ports })
414 }
415}
416impl Serialize for SelectionInformation<'_> {
417 type Error = Error;
418 fn serialized_len(&self) -> usize {
419 let mut n = SELECTION_TIME_LEN;
420 for p in &self.ports {
421 n += 2 + p.in_signal_desc.len() + 2;
422 for o in &p.outputs {
423 n += 2 + o.out_signal_desc.len();
424 }
425 }
426 n
427 }
428 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
429 let total = self.serialized_len();
430 if buf.len() < total {
431 return Err(Error::OutputBufferTooSmall {
432 need: total,
433 have: buf.len(),
434 });
435 }
436 if self.time.len() != SELECTION_TIME_LEN {
437 return Err(Error::InvalidObject {
438 what: "SelectionInformation",
439 reason: "time must be exactly 5 bytes",
440 });
441 }
442 let mut pos = 0;
443 buf[pos..pos + SELECTION_TIME_LEN].copy_from_slice(self.time);
444 pos += SELECTION_TIME_LEN;
445 for p in &self.ports {
446 if p.in_signal_desc.len() > u8::MAX as usize {
447 return Err(Error::InvalidObject {
448 what: "SelectionInformation",
449 reason: "in_signal_desc longer than 255 bytes",
450 });
451 }
452 buf[pos] = p.in_port_id;
453 buf[pos + 1] = p.in_signal_desc.len() as u8;
454 pos += 2;
455 buf[pos..pos + p.in_signal_desc.len()].copy_from_slice(p.in_signal_desc);
456 pos += p.in_signal_desc.len();
457 let mut length_outputs = 0usize;
459 for o in &p.outputs {
460 length_outputs += 2 + o.out_signal_desc.len();
461 }
462 if length_outputs > 0x0FFF {
463 return Err(Error::InvalidObject {
464 what: "SelectionInformation",
465 reason: "length_outputs exceeds 12-bit maximum",
466 });
467 }
468 buf[pos] = ((length_outputs >> 8) & 0x0F) as u8;
470 buf[pos + 1] = (length_outputs & 0xFF) as u8;
471 pos += 2;
472 for o in &p.outputs {
473 if o.out_signal_desc.len() > u8::MAX as usize {
474 return Err(Error::InvalidObject {
475 what: "SelectionInformation",
476 reason: "out_signal_desc longer than 255 bytes",
477 });
478 }
479 buf[pos] = o.out_port_id;
480 buf[pos + 1] = o.out_signal_desc.len() as u8;
481 pos += 2;
482 buf[pos..pos + o.out_signal_desc.len()].copy_from_slice(o.out_signal_desc);
483 pos += o.out_signal_desc.len();
484 }
485 }
486 Ok(pos)
487 }
488}
489
490#[derive(Debug, Clone, PartialEq, Eq, Default)]
492#[cfg_attr(feature = "serde", derive(serde::Serialize))]
493pub struct PortDescription<'a> {
494 pub in_port_id: u8,
496 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
498 pub in_port_desc: &'a [u8],
499 pub out_port_id: u8,
501 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
503 pub out_port_desc: &'a [u8],
504}
505
506#[derive(Debug, Clone, PartialEq, Eq, Default)]
509#[cfg_attr(feature = "serde", derive(serde::Serialize))]
510pub struct PortProfile<'a> {
511 #[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
514 pub receiver_identification: &'a [u8],
515 pub ports: Vec<PortDescription<'a>>,
517}
518
519impl<'a> Parse<'a> for PortProfile<'a> {
520 type Error = Error;
521 fn parse(body: &'a [u8]) -> Result<Self> {
522 if body.is_empty() {
523 return Err(Error::BufferTooShort {
524 need: 1,
525 have: 0,
526 what: "PortProfile.receiver_identification_length",
527 });
528 }
529 let id_len = body[0] as usize;
530 if body.len() < 1 + id_len {
531 return Err(Error::BufferTooShort {
532 need: 1 + id_len,
533 have: body.len(),
534 what: "PortProfile.receiver_identification",
535 });
536 }
537 let receiver_identification = &body[1..1 + id_len];
538 let mut rest = &body[1 + id_len..];
539 let mut ports = Vec::new();
540 while !rest.is_empty() {
541 if rest.len() < 2 {
543 return Err(Error::BufferTooShort {
544 need: 2,
545 have: rest.len(),
546 what: "PortProfile.in_port",
547 });
548 }
549 let in_port_id = rest[0];
550 let in_len = rest[1] as usize;
551 let after_in = 2 + in_len;
552 if rest.len() < after_in + 2 {
554 return Err(Error::BufferTooShort {
555 need: after_in + 2,
556 have: rest.len(),
557 what: "PortProfile.out_port",
558 });
559 }
560 let in_port_desc = &rest[2..after_in];
561 let out_port_id = rest[after_in];
562 let out_len = rest[after_in + 1] as usize;
563 let after_out = after_in + 2 + out_len;
564 if rest.len() < after_out {
565 return Err(Error::BufferTooShort {
566 need: after_out,
567 have: rest.len(),
568 what: "PortProfile.out_port_desc",
569 });
570 }
571 ports.push(PortDescription {
572 in_port_id,
573 in_port_desc,
574 out_port_id,
575 out_port_desc: &rest[after_in + 2..after_out],
576 });
577 rest = &rest[after_out..];
578 }
579 Ok(Self {
580 receiver_identification,
581 ports,
582 })
583 }
584}
585impl Serialize for PortProfile<'_> {
586 type Error = Error;
587 fn serialized_len(&self) -> usize {
588 let mut n = 1 + self.receiver_identification.len();
589 for p in &self.ports {
590 n += 2 + p.in_port_desc.len() + 2 + p.out_port_desc.len();
591 }
592 n
593 }
594 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
595 let total = self.serialized_len();
596 if buf.len() < total {
597 return Err(Error::OutputBufferTooSmall {
598 need: total,
599 have: buf.len(),
600 });
601 }
602 if self.receiver_identification.len() > u8::MAX as usize {
603 return Err(Error::InvalidObject {
604 what: "PortProfile",
605 reason: "receiver_identification longer than 255 bytes",
606 });
607 }
608 let mut pos = 0;
609 buf[pos] = self.receiver_identification.len() as u8;
610 pos += 1;
611 buf[pos..pos + self.receiver_identification.len()]
612 .copy_from_slice(self.receiver_identification);
613 pos += self.receiver_identification.len();
614 for p in &self.ports {
615 if p.in_port_desc.len() > u8::MAX as usize || p.out_port_desc.len() > u8::MAX as usize {
616 return Err(Error::InvalidObject {
617 what: "PortProfile",
618 reason: "port description longer than 255 bytes",
619 });
620 }
621 buf[pos] = p.in_port_id;
622 buf[pos + 1] = p.in_port_desc.len() as u8;
623 pos += 2;
624 buf[pos..pos + p.in_port_desc.len()].copy_from_slice(p.in_port_desc);
625 pos += p.in_port_desc.len();
626 buf[pos] = p.out_port_id;
627 buf[pos + 1] = p.out_port_desc.len() as u8;
628 pos += 2;
629 buf[pos..pos + p.out_port_desc.len()].copy_from_slice(p.out_port_desc);
630 pos += p.out_port_desc.len();
631 }
632 Ok(pos)
633 }
634}
635
636#[derive(Debug, Clone, PartialEq, Eq, Default)]
638#[cfg_attr(feature = "serde", derive(serde::Serialize))]
639pub struct ViewedService {
640 pub service_id: u16,
642 pub component_tags: Vec<u8>,
644}
645
646impl<'a> Parse<'a> for ViewedService {
647 type Error = Error;
648 fn parse(body: &'a [u8]) -> Result<Self> {
649 if body.len() < 3 {
651 return Err(Error::BufferTooShort {
652 need: 3,
653 have: body.len(),
654 what: "ViewedService",
655 });
656 }
657 let service_id = u16::from_be_bytes([body[0], body[1]]);
658 let n = body[2] as usize;
659 if body.len() < 3 + n {
660 return Err(Error::BufferTooShort {
661 need: 3 + n,
662 have: body.len(),
663 what: "ViewedService.component_tags",
664 });
665 }
666 Ok(Self {
667 service_id,
668 component_tags: body[3..3 + n].to_vec(),
669 })
670 }
671}
672impl Serialize for ViewedService {
673 type Error = Error;
674 fn serialized_len(&self) -> usize {
675 3 + self.component_tags.len()
676 }
677 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
678 let total = self.serialized_len();
679 if buf.len() < total {
680 return Err(Error::OutputBufferTooSmall {
681 need: total,
682 have: buf.len(),
683 });
684 }
685 if self.component_tags.len() > u8::MAX as usize {
686 return Err(Error::InvalidObject {
687 what: "ViewedService",
688 reason: "more than 255 component tags",
689 });
690 }
691 buf[0..2].copy_from_slice(&self.service_id.to_be_bytes());
692 buf[2] = self.component_tags.len() as u8;
693 buf[3..3 + self.component_tags.len()].copy_from_slice(&self.component_tags);
694 Ok(total)
695 }
696}
697
698#[derive(Debug, Clone, Copy, PartialEq, Eq)]
700#[cfg_attr(feature = "serde", derive(serde::Serialize))]
701#[non_exhaustive]
702pub enum ActivationState {
703 StandbyActive,
705 On,
707 Reserved(u8),
709}
710
711impl ActivationState {
712 #[must_use]
714 pub fn from_u8(v: u8) -> Self {
715 match v {
716 1 => Self::StandbyActive,
717 2 => Self::On,
718 other => Self::Reserved(other),
719 }
720 }
721 #[must_use]
723 pub const fn to_u8(self) -> u8 {
724 match self {
725 Self::StandbyActive => 1,
726 Self::On => 2,
727 Self::Reserved(v) => v & 0x07,
728 }
729 }
730 #[must_use]
732 pub fn name(&self) -> &'static str {
733 match self {
734 Self::StandbyActive => "Standby-active",
735 Self::On => "On",
736 Self::Reserved(_) => "reserved",
737 }
738 }
739}
740broadcast_common::impl_spec_display!(ActivationState, Reserved);
741
742#[derive(Debug, Clone, Copy, PartialEq, Eq)]
744#[cfg_attr(feature = "serde", derive(serde::Serialize))]
745pub struct ActivationStatus {
746 pub event_activated: bool,
748 pub activation_state: ActivationState,
752 pub reserved: u8,
755}
756
757impl ActivationStatus {
758 const EVENT_ACTIVATED_BIT: u8 = 0x08;
759 const ACTIVATION_STATE_MASK: u8 = 0x07;
760 const RESERVED_SHIFT: u32 = 4;
762 const RESERVED_MASK: u8 = 0x0F;
763}
764
765impl<'a> Parse<'a> for ActivationStatus {
766 type Error = Error;
767 fn parse(body: &'a [u8]) -> Result<Self> {
768 if body.is_empty() {
769 return Err(Error::BufferTooShort {
770 need: 1,
771 have: 0,
772 what: "ActivationStatus",
773 });
774 }
775 Ok(Self {
776 event_activated: body[0] & Self::EVENT_ACTIVATED_BIT != 0,
777 activation_state: ActivationState::from_u8(body[0] & Self::ACTIVATION_STATE_MASK),
778 reserved: body[0] >> Self::RESERVED_SHIFT,
779 })
780 }
781}
782impl Serialize for ActivationStatus {
783 type Error = Error;
784 fn serialized_len(&self) -> usize {
785 1
786 }
787 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
788 if buf.is_empty() {
789 return Err(Error::OutputBufferTooSmall { need: 1, have: 0 });
790 }
791 let mut b = 0u8;
792 if self.event_activated {
793 b |= Self::EVENT_ACTIVATED_BIT;
794 }
795 b |= self.activation_state.to_u8() & Self::ACTIVATION_STATE_MASK;
796 b |= (self.reserved & Self::RESERVED_MASK) << Self::RESERVED_SHIFT;
797 buf[0] = b;
798 Ok(1)
799 }
800}
801
802crate::dispatch::declare_resource_apdus! {
803 #[derive(Debug, Clone, PartialEq, Eq)]
805 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
806 #[non_exhaustive]
807 pub enum StatusQueryApdu<'a> ("status_query") {
808 StatusQueryReq(StatusQueryReq) = tag::STATUS_QUERY_REQ,
810 TrapReq(TrapReq) = tag::TRAP_REQ,
812 GetNextItemReq(GetNextItemReq) = tag::GET_NEXT_ITEM_REQ,
814 GetNextItemAck(GetNextItemAck) = tag::GET_NEXT_ITEM_ACK,
816 StatusAck(StatusAck<'a>) = tag::STATUS_ACK,
818 }
819}
820
821#[cfg(test)]
822mod tests {
823 use super::*;
824
825 #[test]
826 fn status_query_req_round_trips_and_bites() {
827 let req = StatusQueryReq {
828 length_form: crate::length::LengthForm::Minimal,
829 status_item: StatusItem::PortProfile,
830 };
831 let bytes = req.try_to_bytes().unwrap();
832 assert_eq!(bytes, [0x9F, 0x80, 0x00, 0x04, 0x00, 0x00, 0x00, 0x02]);
834 assert_eq!(StatusQueryReq::parse(&bytes).unwrap(), req);
835 let other = StatusQueryReq {
836 length_form: crate::length::LengthForm::Minimal,
837 status_item: StatusItem::ViewedService,
838 };
839 assert_ne!(bytes, other.try_to_bytes().unwrap());
840 }
841
842 #[test]
843 fn trap_and_getnext_round_trip() {
844 let trap = TrapReq {
845 length_form: crate::length::LengthForm::Minimal,
846 status_item: StatusItem::ActivationStatus,
847 };
848 assert_eq!(
849 trap.try_to_bytes().unwrap(),
850 [0x9F, 0x80, 0x01, 0x04, 0, 0, 0, 4]
851 );
852 assert_eq!(TrapReq::parse(&trap.try_to_bytes().unwrap()).unwrap(), trap);
853
854 let gnr = GetNextItemReq {
855 length_form: crate::length::LengthForm::Minimal,
856 start_status_item: 0x0000_0007,
857 };
858 assert_eq!(
859 gnr.try_to_bytes().unwrap(),
860 [0x9F, 0x80, 0x02, 0x04, 0, 0, 0, 7]
861 );
862 assert_eq!(
863 GetNextItemReq::parse(&gnr.try_to_bytes().unwrap()).unwrap(),
864 gnr
865 );
866
867 let gna = GetNextItemAck {
868 length_form: crate::length::LengthForm::Minimal,
869 next_status_item: 0x0000_0002,
870 };
871 assert_eq!(
872 gna.try_to_bytes().unwrap(),
873 [0x9F, 0x80, 0x03, 0x04, 0, 0, 0, 2]
874 );
875 assert_eq!(
876 GetNextItemAck::parse(&gna.try_to_bytes().unwrap()).unwrap(),
877 gna
878 );
879 }
880
881 #[test]
882 fn status_item_reserved_round_trips() {
883 let req = StatusQueryReq {
884 length_form: crate::length::LengthForm::Minimal,
885 status_item: StatusItem::from_u32(0),
886 };
887 assert_eq!(req.status_item, StatusItem::Reserved(0));
888 assert_eq!(req.status_item.name(), "reserved");
889 assert_eq!(
890 StatusQueryReq::parse(&req.try_to_bytes().unwrap()).unwrap(),
891 req
892 );
893 }
894
895 #[test]
896 fn status_ack_with_status_bytes_round_trips_and_bites() {
897 let ack = StatusAck {
898 length_form: crate::length::LengthForm::Minimal,
899 status_item: StatusItem::ViewedService,
900 status_bytes: &[0x01, 0x23, 0x00],
901 };
902 let bytes = ack.try_to_bytes().unwrap();
903 assert_eq!(
905 bytes,
906 [0x9F, 0x80, 0x04, 0x07, 0, 0, 0, 3, 0x01, 0x23, 0x00]
907 );
908 assert_eq!(StatusAck::parse(&bytes).unwrap(), ack);
909 let mut other = ack.clone();
910 other.status_bytes = &[0x01, 0x23, 0x01];
911 assert_ne!(bytes, other.try_to_bytes().unwrap());
912 }
913
914 #[test]
915 fn status_ack_empty_status_bytes_means_unsupported() {
916 let ack = StatusAck {
917 length_form: crate::length::LengthForm::Minimal,
918 status_item: StatusItem::PortProfile,
919 status_bytes: &[],
920 };
921 let bytes = ack.try_to_bytes().unwrap();
922 assert_eq!(bytes, [0x9F, 0x80, 0x04, 0x04, 0, 0, 0, 2]);
923 assert_eq!(StatusAck::parse(&bytes).unwrap(), ack);
924 }
925
926 #[test]
927 fn selection_information_multi_port_round_trips_and_bites() {
928 let si = SelectionInformation {
930 time: &[0x20, 0x06, 0x18, 0x12, 0x00],
931 ports: alloc::vec![
932 InPortDescription {
933 in_port_id: 0x01,
934 in_signal_desc: &[0xAA, 0xBB],
935 outputs: alloc::vec![
936 OutputSignal {
937 out_port_id: 0x00,
938 out_signal_desc: &[0x02],
939 },
940 OutputSignal {
941 out_port_id: 0x01,
942 out_signal_desc: &[0x01],
943 },
944 ],
945 },
946 InPortDescription {
947 in_port_id: 0x18,
948 in_signal_desc: &[],
949 outputs: alloc::vec![],
950 },
951 ],
952 };
953 let bytes = si.try_to_bytes().unwrap();
954 assert_eq!(SelectionInformation::parse(&bytes).unwrap(), si);
955 let mut other = si.clone();
957 other.ports[0].outputs[1].out_port_id = 0x02;
958 assert_ne!(bytes, other.try_to_bytes().unwrap());
959 assert_eq!(
963 bytes,
964 [
965 0x20, 0x06, 0x18, 0x12, 0x00, 0x01, 0x02, 0xAA, 0xBB, 0x00, 0x06, 0x00, 0x01, 0x02, 0x01, 0x01, 0x01, 0x18, 0x00, 0x00, 0x00, ]
973 );
974 }
975
976 #[test]
977 fn port_profile_multi_port_round_trips_and_bites() {
978 let pp = PortProfile {
979 receiver_identification: b"ACME-TV1",
980 ports: alloc::vec![
981 PortDescription {
982 in_port_id: 0x00,
983 in_port_desc: b"RF0",
984 out_port_id: 0x00,
985 out_port_desc: b"D0",
986 },
987 PortDescription {
988 in_port_id: 0x18,
989 in_port_desc: b"CI",
990 out_port_id: 0x10,
991 out_port_desc: b"SCART",
992 },
993 ],
994 };
995 let bytes = pp.try_to_bytes().unwrap();
996 assert_eq!(PortProfile::parse(&bytes).unwrap(), pp);
997 let mut other = pp.clone();
998 other.ports[1].out_port_id = 0x11;
999 assert_ne!(bytes, other.try_to_bytes().unwrap());
1000 }
1001
1002 #[test]
1003 fn viewed_service_multi_component_round_trips_and_bites() {
1004 let vs = ViewedService {
1005 service_id: 0x1234,
1006 component_tags: alloc::vec![0x10, 0x20, 0x30],
1007 };
1008 let bytes = vs.try_to_bytes().unwrap();
1009 assert_eq!(bytes, [0x12, 0x34, 0x03, 0x10, 0x20, 0x30]);
1010 assert_eq!(ViewedService::parse(&bytes).unwrap(), vs);
1011 let mut other = vs.clone();
1012 other.component_tags[2] = 0x31;
1013 assert_ne!(bytes, other.try_to_bytes().unwrap());
1014 }
1015
1016 #[test]
1017 fn activation_status_packs_bits() {
1018 let a = ActivationStatus {
1019 event_activated: true,
1020 activation_state: ActivationState::On,
1021 reserved: 0,
1022 };
1023 let bytes = a.try_to_bytes().unwrap();
1024 assert_eq!(bytes, [0x0A]);
1026 assert_eq!(ActivationStatus::parse(&bytes).unwrap(), a);
1027 let b = ActivationStatus {
1029 event_activated: false,
1030 activation_state: ActivationState::StandbyActive,
1031 reserved: 0,
1032 };
1033 assert_eq!(b.try_to_bytes().unwrap(), [0x01]);
1034 assert_eq!(ActivationStatus::parse(&[0x01]).unwrap(), b);
1035 for byte in 0x00u8..=0xFF {
1038 let parsed = ActivationStatus::parse(&[byte]).unwrap();
1039 assert_eq!(parsed.try_to_bytes().unwrap(), [byte], "byte {byte:#04X}");
1040 }
1041 }
1042
1043 #[test]
1044 fn dispatch_routes_each_tag() {
1045 let q = StatusQueryReq {
1046 length_form: crate::length::LengthForm::Minimal,
1047 status_item: StatusItem::SelectionInformation,
1048 }
1049 .try_to_bytes()
1050 .unwrap();
1051 assert!(matches!(
1052 StatusQueryApdu::parse(&q).unwrap(),
1053 StatusQueryApdu::StatusQueryReq(_)
1054 ));
1055 let ack = StatusAck {
1056 length_form: crate::length::LengthForm::Minimal,
1057 status_item: StatusItem::ActivationStatus,
1058 status_bytes: &[0x0A],
1059 }
1060 .try_to_bytes()
1061 .unwrap();
1062 let parsed = StatusQueryApdu::parse(&ack).unwrap();
1063 assert!(matches!(parsed, StatusQueryApdu::StatusAck(_)));
1064 assert_eq!(parsed.try_to_bytes().unwrap(), ack);
1065 }
1066}