rakata_core/
resource_id.rs1use std::fmt::{Display, Formatter, LowerHex, UpperHex};
2use thiserror::Error;
3
4#[cfg(feature = "tracing")]
5macro_rules! trace_debug {
6 ($($arg:tt)*) => {
7 tracing::debug!($($arg)*);
8 };
9}
10
11#[cfg(not(feature = "tracing"))]
12macro_rules! trace_debug {
13 ($($arg:tt)*) => {};
14}
15
16pub const RESOURCE_INDEX_MASK: u32 = 0x000F_FFFF;
18pub const MAX_BIF_INDEX: u32 = 0x0000_0FFF;
20
21#[derive(Debug, Clone, PartialEq, Eq, Error)]
23pub enum ResourceIdError {
24 #[error(
26 "resource id parts out of range (bif_index={bif_index}, resource_index={resource_index})"
27 )]
28 InvalidParts {
29 bif_index: u32,
31 resource_index: u32,
33 },
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43pub struct ResourceId(u32);
44
45impl ResourceId {
46 pub const fn from_raw(raw: u32) -> Self {
48 Self(raw)
49 }
50
51 #[cfg_attr(
59 feature = "tracing",
60 tracing::instrument(level = "debug", fields(bif_index, resource_index))
61 )]
62 pub fn from_parts(bif_index: u32, resource_index: u32) -> Result<Self, ResourceIdError> {
63 if bif_index > MAX_BIF_INDEX || resource_index > RESOURCE_INDEX_MASK {
64 trace_debug!("resource id parts are out of range");
65 return Err(ResourceIdError::InvalidParts {
66 bif_index,
67 resource_index,
68 });
69 }
70 let raw = (bif_index << 20) | resource_index;
71 trace_debug!(raw, "packed resource id from parts");
72 Ok(Self(raw))
73 }
74
75 pub const fn raw(self) -> u32 {
77 self.0
78 }
79
80 pub const fn bif_index(self) -> u32 {
82 self.0 >> 20
83 }
84
85 pub const fn resource_index(self) -> u32 {
87 self.0 & RESOURCE_INDEX_MASK
88 }
89}
90
91impl From<u32> for ResourceId {
92 fn from(value: u32) -> Self {
93 Self::from_raw(value)
94 }
95}
96
97impl From<ResourceId> for u32 {
98 fn from(value: ResourceId) -> Self {
99 value.raw()
100 }
101}
102
103impl Display for ResourceId {
104 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
105 write!(f, "{}", self.0)
106 }
107}
108
109impl LowerHex for ResourceId {
110 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
111 LowerHex::fmt(&self.0, f)
112 }
113}
114
115impl UpperHex for ResourceId {
116 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
117 UpperHex::fmt(&self.0, f)
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124
125 #[test]
126 fn from_parts_packs_expected_bit_layout() {
127 let resource_id = ResourceId::from_parts(0xabc, 0x54321).expect("must fit");
128 assert_eq!(resource_id.raw(), 0xabc5_4321);
129 assert_eq!(resource_id.bif_index(), 0xabc);
130 assert_eq!(resource_id.resource_index(), 0x54321);
131 }
132
133 #[test]
134 fn from_parts_rejects_out_of_range_values() {
135 let err = ResourceId::from_parts(0x1000, 0).expect_err("must fail");
136 assert_eq!(
137 err,
138 ResourceIdError::InvalidParts {
139 bif_index: 0x1000,
140 resource_index: 0,
141 }
142 );
143 }
144}