dvb_ci/objects/
ca_info.rs1use crate::error::{Error, Result};
7use crate::tag::{self, ApduTag};
8use crate::traits::ApduDef;
9use alloc::vec::Vec;
10use broadcast_common::{Parse, Serialize};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
14#[cfg_attr(feature = "serde", derive(serde::Serialize))]
15pub struct CaInfoEnq;
16
17#[derive(Debug, Clone, PartialEq, Eq, Default)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize))]
20pub struct CaInfo {
21 pub ca_system_ids: Vec<u16>,
23}
24
25impl<'a> Parse<'a> for CaInfoEnq {
26 type Error = Error;
27 fn parse(bytes: &'a [u8]) -> Result<Self> {
28 super::parse_empty_apdu(bytes, tag::CA_INFO_ENQ, "ca_info_enq")?;
29 Ok(Self)
30 }
31}
32impl Serialize for CaInfoEnq {
33 type Error = Error;
34 fn serialized_len(&self) -> usize {
35 super::empty_apdu_len()
36 }
37 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
38 super::serialize_empty_apdu(tag::CA_INFO_ENQ, buf)
39 }
40}
41impl ApduDef<'_> for CaInfoEnq {
42 const TAG: ApduTag = tag::CA_INFO_ENQ;
43 const NAME: &'static str = "CA_INFO_ENQ";
44}
45
46impl<'a> Parse<'a> for CaInfo {
47 type Error = Error;
48 fn parse(bytes: &'a [u8]) -> Result<Self> {
49 let body = super::parse_apdu_header(bytes, tag::CA_INFO, "ca_info")?;
50 if body.len() % 2 != 0 {
51 return Err(Error::InvalidObject {
52 what: "ca_info",
53 reason: "body length is not a multiple of 2",
54 });
55 }
56 let ca_system_ids = body
57 .chunks_exact(2)
58 .map(|c| u16::from_be_bytes([c[0], c[1]]))
59 .collect();
60 Ok(Self { ca_system_ids })
61 }
62}
63
64impl Serialize for CaInfo {
65 type Error = Error;
66 fn serialized_len(&self) -> usize {
67 super::apdu_len(self.ca_system_ids.len() * 2)
68 }
69 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
70 let body_len = self.ca_system_ids.len() * 2;
71 let mut pos = super::write_apdu_header(tag::CA_INFO, body_len, buf)?;
72 for id in &self.ca_system_ids {
73 buf[pos..pos + 2].copy_from_slice(&id.to_be_bytes());
74 pos += 2;
75 }
76 Ok(pos)
77 }
78}
79
80impl ApduDef<'_> for CaInfo {
81 const TAG: ApduTag = tag::CA_INFO;
82 const NAME: &'static str = "CA_INFO";
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn enq_round_trip() {
91 let bytes = CaInfoEnq.to_bytes();
92 assert_eq!(bytes, [0x9F, 0x80, 0x30, 0x00]);
93 assert_eq!(CaInfoEnq::parse(&bytes).unwrap(), CaInfoEnq);
94 }
95
96 #[test]
97 fn ca_info_multi_round_trips() {
98 let info = CaInfo {
99 ca_system_ids: alloc::vec![0x0500, 0x0B00, 0x1801],
100 };
101 let bytes = info.to_bytes();
102 assert_eq!(&bytes[..4], &[0x9F, 0x80, 0x31, 0x06]); let parsed = CaInfo::parse(&bytes).unwrap();
104 assert_eq!(parsed, info);
105 assert_eq!(parsed.ca_system_ids.len(), 3);
106 }
107
108 #[test]
109 fn mutating_id_changes_bytes() {
110 let info = CaInfo {
111 ca_system_ids: alloc::vec![0x0500],
112 };
113 let a = info.to_bytes();
114 let mut other = info.clone();
115 other.ca_system_ids[0] = 0x0B00;
116 assert_ne!(a, other.to_bytes());
117 }
118}