1use crate::error::{Error, Result};
12use broadcast_common::len::need_out;
13use broadcast_common::{Parse, Serialize};
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
17#[cfg_attr(feature = "serde", derive(serde::Serialize))]
18#[cfg_attr(feature = "serde", serde(transparent))]
19pub struct ResourceId(pub u32);
20
21impl ResourceId {
22 pub const LEN: usize = 4;
24
25 #[must_use]
28 pub const fn id_type(self) -> u8 {
29 (self.0 >> 30) as u8
30 }
31
32 #[must_use]
34 pub const fn is_private(self) -> bool {
35 self.id_type() == 3
36 }
37
38 #[must_use]
41 pub const fn resource_class(self) -> u16 {
42 ((self.0 >> 16) & 0x3FFF) as u16
43 }
44
45 #[must_use]
47 pub const fn resource_type(self) -> u16 {
48 ((self.0 >> 6) & 0x03FF) as u16
49 }
50
51 #[must_use]
53 pub const fn resource_version(self) -> u8 {
54 (self.0 & 0x3F) as u8
55 }
56
57 #[must_use]
60 pub fn name(self) -> &'static str {
61 match self {
62 RESOURCE_MANAGER => "resource_manager",
63 APPLICATION_INFORMATION => "application_information",
64 CONDITIONAL_ACCESS_SUPPORT => "conditional_access_support",
65 HOST_CONTROL => "host_control",
66 DATE_TIME => "date_time",
67 MMI => "mmi",
68 _ => "unknown",
69 }
70 }
71}
72
73impl core::fmt::Display for ResourceId {
74 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
75 match self.name() {
76 "unknown" => write!(f, "resource_id(0x{:08X})", self.0),
77 n => write!(f, "{n}(0x{:08X})", self.0),
78 }
79 }
80}
81
82impl<'a> Parse<'a> for ResourceId {
83 type Error = Error;
84 fn parse(bytes: &'a [u8]) -> Result<Self> {
85 let chunk = bytes.first_chunk::<4>().ok_or(Error::BufferTooShort(
86 broadcast_common::error::BufferTooShort {
87 need: 4,
88 have: bytes.len(),
89 what: "resource_identifier",
90 },
91 ))?;
92 Ok(Self(u32::from_be_bytes(*chunk)))
93 }
94}
95
96impl Serialize for ResourceId {
97 type Error = Error;
98 fn serialized_len(&self) -> usize {
99 Self::LEN
100 }
101 fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
102 need_out(buf, Self::LEN)?;
103 buf[..Self::LEN].copy_from_slice(&self.0.to_be_bytes());
104 Ok(Self::LEN)
105 }
106}
107
108pub const RESOURCE_MANAGER: ResourceId = ResourceId(0x0001_0041);
110pub const APPLICATION_INFORMATION: ResourceId = ResourceId(0x0002_0041);
112pub const CONDITIONAL_ACCESS_SUPPORT: ResourceId = ResourceId(0x0003_0041);
114pub const HOST_CONTROL: ResourceId = ResourceId(0x0020_0041);
116pub const DATE_TIME: ResourceId = ResourceId(0x0024_0041);
118pub const MMI: ResourceId = ResourceId(0x0040_0041);
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn packs_per_table_57() {
127 let rm = RESOURCE_MANAGER;
129 assert_eq!(rm.id_type(), 0);
130 assert!(!rm.is_private());
131 assert_eq!(rm.resource_class(), 1);
132 assert_eq!(rm.resource_type(), 1);
133 assert_eq!(rm.resource_version(), 1);
134 assert_eq!(rm.name(), "resource_manager");
135 }
136
137 #[test]
138 fn ca_support_fields() {
139 assert_eq!(CONDITIONAL_ACCESS_SUPPORT.resource_class(), 3);
140 assert_eq!(
141 CONDITIONAL_ACCESS_SUPPORT.name(),
142 "conditional_access_support"
143 );
144 }
145
146 #[test]
147 fn private_resource_type() {
148 let p = ResourceId(0xC000_0000);
149 assert!(p.is_private());
150 assert_eq!(p.id_type(), 3);
151 assert_eq!(p.name(), "unknown");
152 }
153
154 #[test]
155 fn round_trip() {
156 let r = DATE_TIME;
157 let bytes = r.try_to_bytes().unwrap();
158 assert_eq!(bytes, [0x00, 0x24, 0x00, 0x41]);
159 assert_eq!(ResourceId::parse(&bytes).unwrap(), r);
160 }
161
162 #[test]
163 fn mutating_changes_bytes() {
164 let mut bytes = MMI.try_to_bytes().unwrap();
165 let a = ResourceId::parse(&bytes).unwrap();
166 bytes[0] ^= 0xFF;
167 let b = ResourceId::parse(&bytes).unwrap();
168 assert_ne!(a, b);
169 }
170}