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(
53 feature = "tracing",
54 tracing::instrument(level = "debug", fields(bif_index, resource_index))
55 )]
56 pub fn from_parts(bif_index: u32, resource_index: u32) -> Result<Self, ResourceIdError> {
57 if bif_index > MAX_BIF_INDEX || resource_index > RESOURCE_INDEX_MASK {
58 trace_debug!("resource id parts are out of range");
59 return Err(ResourceIdError::InvalidParts {
60 bif_index,
61 resource_index,
62 });
63 }
64 let raw = (bif_index << 20) | resource_index;
65 trace_debug!(raw, "packed resource id from parts");
66 Ok(Self(raw))
67 }
68
69 pub const fn raw(self) -> u32 {
71 self.0
72 }
73
74 pub const fn bif_index(self) -> u32 {
76 self.0 >> 20
77 }
78
79 pub const fn resource_index(self) -> u32 {
81 self.0 & RESOURCE_INDEX_MASK
82 }
83}
84
85impl From<u32> for ResourceId {
86 fn from(value: u32) -> Self {
87 Self::from_raw(value)
88 }
89}
90
91impl From<ResourceId> for u32 {
92 fn from(value: ResourceId) -> Self {
93 value.raw()
94 }
95}
96
97impl Display for ResourceId {
98 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
99 write!(f, "{}", self.0)
100 }
101}
102
103impl LowerHex for ResourceId {
104 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
105 LowerHex::fmt(&self.0, f)
106 }
107}
108
109impl UpperHex for ResourceId {
110 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
111 UpperHex::fmt(&self.0, f)
112 }
113}
114
115#[cfg(test)]
116mod tests {
117 use super::*;
118
119 #[test]
120 fn from_parts_packs_expected_bit_layout() {
121 let resource_id = ResourceId::from_parts(0xabc, 0x54321).expect("must fit");
122 assert_eq!(resource_id.raw(), 0xabc5_4321);
123 assert_eq!(resource_id.bif_index(), 0xabc);
124 assert_eq!(resource_id.resource_index(), 0x54321);
125 }
126
127 #[test]
128 fn from_parts_rejects_out_of_range_values() {
129 let err = ResourceId::from_parts(0x1000, 0).expect_err("must fail");
130 assert_eq!(
131 err,
132 ResourceIdError::InvalidParts {
133 bif_index: 0x1000,
134 resource_index: 0,
135 }
136 );
137 }
138}