dvb_ci/objects/
resource_manager.rs1use crate::error::{Error, Result};
8use crate::resource::ResourceId;
9use crate::tag::{self, ApduTag};
10use crate::traits::ApduDef;
11use alloc::vec::Vec;
12use broadcast_common::{Parse, Serialize};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize))]
17pub struct ProfileEnq;
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
21#[cfg_attr(feature = "serde", derive(serde::Serialize))]
22pub struct ProfileChange;
23
24#[derive(Debug, Clone, PartialEq, Eq, Default)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize))]
27pub struct Profile {
28 pub resources: Vec<ResourceId>,
30}
31
32impl<'a> Parse<'a> for ProfileEnq {
35 type Error = Error;
36 fn parse(bytes: &'a [u8]) -> Result<Self> {
37 super::parse_empty_apdu(bytes, tag::PROFILE_ENQ, "profile_enq")?;
38 Ok(Self)
39 }
40}
41impl Serialize for ProfileEnq {
42 type Error = Error;
43 fn serialized_len(&self) -> usize {
44 super::empty_apdu_len()
45 }
46 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
47 super::serialize_empty_apdu(tag::PROFILE_ENQ, buf)
48 }
49}
50impl ApduDef<'_> for ProfileEnq {
51 const TAG: ApduTag = tag::PROFILE_ENQ;
52 const NAME: &'static str = "PROFILE_ENQ";
53}
54
55impl<'a> Parse<'a> for ProfileChange {
58 type Error = Error;
59 fn parse(bytes: &'a [u8]) -> Result<Self> {
60 super::parse_empty_apdu(bytes, tag::PROFILE_CHANGE, "profile_change")?;
61 Ok(Self)
62 }
63}
64impl Serialize for ProfileChange {
65 type Error = Error;
66 fn serialized_len(&self) -> usize {
67 super::empty_apdu_len()
68 }
69 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
70 super::serialize_empty_apdu(tag::PROFILE_CHANGE, buf)
71 }
72}
73impl ApduDef<'_> for ProfileChange {
74 const TAG: ApduTag = tag::PROFILE_CHANGE;
75 const NAME: &'static str = "PROFILE_CHANGE";
76}
77
78impl<'a> Parse<'a> for Profile {
81 type Error = Error;
82 fn parse(bytes: &'a [u8]) -> Result<Self> {
83 let body = super::parse_apdu_header(bytes, tag::PROFILE, "profile")?;
84 if body.len() % ResourceId::LEN != 0 {
85 return Err(Error::InvalidObject {
86 what: "profile",
87 reason: "body length is not a multiple of 4",
88 });
89 }
90 let mut resources = Vec::with_capacity(body.len() / ResourceId::LEN);
91 for chunk in body.chunks_exact(ResourceId::LEN) {
92 resources.push(ResourceId::parse(chunk)?);
93 }
94 Ok(Self { resources })
95 }
96}
97
98impl Serialize for Profile {
99 type Error = Error;
100 fn serialized_len(&self) -> usize {
101 let body = self.resources.len() * ResourceId::LEN;
102 super::apdu_len(body)
103 }
104 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
105 let body_len = self.resources.len() * ResourceId::LEN;
106 let mut pos = super::write_apdu_header(tag::PROFILE, body_len, buf)?;
107 for r in &self.resources {
108 pos += r.serialize_into(&mut buf[pos..])?;
109 }
110 Ok(pos)
111 }
112}
113
114impl ApduDef<'_> for Profile {
115 const TAG: ApduTag = tag::PROFILE;
116 const NAME: &'static str = "PROFILE";
117}
118
119#[cfg(test)]
120mod tests {
121 use super::*;
122 use crate::resource::{APPLICATION_INFORMATION, CONDITIONAL_ACCESS_SUPPORT, RESOURCE_MANAGER};
123
124 #[test]
125 fn profile_enq_round_trip() {
126 let bytes = ProfileEnq.to_bytes();
127 assert_eq!(bytes, [0x9F, 0x80, 0x10, 0x00]);
128 assert_eq!(ProfileEnq::parse(&bytes).unwrap(), ProfileEnq);
129 }
130
131 #[test]
132 fn profile_change_round_trip() {
133 let bytes = ProfileChange.to_bytes();
134 assert_eq!(bytes, [0x9F, 0x80, 0x12, 0x00]);
135 assert_eq!(ProfileChange::parse(&bytes).unwrap(), ProfileChange);
136 }
137
138 #[test]
139 fn profile_reply_multi_resource_round_trips() {
140 let p = Profile {
141 resources: alloc::vec![
142 RESOURCE_MANAGER,
143 APPLICATION_INFORMATION,
144 CONDITIONAL_ACCESS_SUPPORT,
145 ],
146 };
147 let bytes = p.to_bytes();
148 assert_eq!(bytes.len(), 16);
150 assert_eq!(&bytes[..4], &[0x9F, 0x80, 0x11, 0x0C]); let parsed = Profile::parse(&bytes).unwrap();
152 assert_eq!(parsed, p);
153 assert_eq!(parsed.resources.len(), 3);
154 }
155
156 #[test]
157 fn mutating_resource_changes_bytes() {
158 let p = Profile {
159 resources: alloc::vec![RESOURCE_MANAGER],
160 };
161 let bytes = p.to_bytes();
162 let mut other = p.clone();
163 other.resources[0] = MMI_RES;
164 assert_ne!(bytes, other.to_bytes());
165 }
166
167 const MMI_RES: ResourceId = crate::resource::MMI;
168}