cityjson_types/resources/
handles.rs1use crate::resources::id::ResourceId32;
29use std::fmt::{Display, Formatter};
30use std::hash::Hash;
31
32#[allow(dead_code)]
34pub(crate) trait HandleType: Copy + Clone + PartialEq + Eq + Hash + Default {
35 fn from_raw(raw: ResourceId32) -> Self;
36 fn to_raw(self) -> ResourceId32;
37
38 fn is_null(self) -> bool {
39 let raw = self.to_raw();
40 raw.index() == 0 && raw.generation() == 0
41 }
42}
43
44macro_rules! define_handle {
46 (
47 $(#[$meta:meta])*
48 $name:ident
49 ) => {
50 $(#[$meta])*
51 #[repr(transparent)]
52 #[derive(Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
53 pub struct $name(ResourceId32);
54
55 #[allow(dead_code)]
56 impl $name {
57 #[must_use]
58 pub fn is_null(self) -> bool {
59 self.0.index() == 0 && self.0.generation() == 0
60 }
61
62 pub(crate) fn from_parts(index: u32, generation: u16) -> Self {
63 Self(ResourceId32::new(index, generation))
64 }
65
66 pub(crate) fn index(self) -> u32 {
67 self.0.index()
68 }
69
70 pub(crate) fn generation(self) -> u16 {
71 self.0.generation()
72 }
73
74 #[must_use]
79 pub fn raw_parts(self) -> (u32, u16) {
80 self.to_raw_parts()
81 }
82
83 #[must_use]
95 pub unsafe fn from_raw_parts_unchecked(index: u32, generation: u16) -> Self {
96 Self::from_raw_parts(index, generation)
97 }
98
99 pub(crate) fn from_raw_parts(index: u32, generation: u16) -> Self {
100 Self(ResourceId32::new(index, generation))
101 }
102
103 pub(crate) fn to_raw_parts(self) -> (u32, u16) {
104 (self.0.index(), self.0.generation())
105 }
106
107 pub(crate) fn to_raw(self) -> ResourceId32 {
108 self.0
109 }
110
111 pub(crate) fn from_raw(raw: ResourceId32) -> Self {
112 Self(raw)
113 }
114 }
115
116 impl Display for $name {
117 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
118 write!(f, "{}", stringify!($name))
119 }
120 }
121
122 impl std::fmt::Debug for $name {
123 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
124 write!(f, "{}(index={}, generation={})", stringify!($name), self.0.index(), self.0.generation())
125 }
126 }
127
128 impl HandleType for $name {
129 fn from_raw(raw: ResourceId32) -> Self {
130 Self(raw)
131 }
132
133 fn to_raw(self) -> ResourceId32 {
134 self.0
135 }
136 }
137 };
138}
139
140define_handle! {
141 GeometryHandle
143}
144
145define_handle! {
146 GeometryTemplateHandle
148}
149
150define_handle! {
151 SemanticHandle
153}
154
155define_handle! {
156 MaterialHandle
158}
159
160define_handle! {
161 TextureHandle
163}
164
165define_handle! {
166 CityObjectHandle
168}
169
170#[inline]
171pub(crate) fn cast_handle_slice<H: HandleType>(raw: &[ResourceId32]) -> &[H] {
172 const {
173 assert!(std::mem::size_of::<H>() == std::mem::size_of::<ResourceId32>());
174 assert!(std::mem::align_of::<H>() == std::mem::align_of::<ResourceId32>());
175 }
176
177 unsafe { std::slice::from_raw_parts(raw.as_ptr().cast::<H>(), raw.len()) }
180}
181
182#[inline]
183pub(crate) fn cast_option_handle_slice<H: HandleType>(
184 raw: &[Option<ResourceId32>],
185) -> &[Option<H>] {
186 const {
187 assert!(std::mem::size_of::<Option<H>>() == std::mem::size_of::<Option<ResourceId32>>());
188 assert!(std::mem::align_of::<Option<H>>() == std::mem::align_of::<Option<ResourceId32>>());
189 }
190
191 unsafe { std::slice::from_raw_parts(raw.as_ptr().cast::<Option<H>>(), raw.len()) }
194}
195
196#[cfg(test)]
197mod tests {
198 use super::{GeometryHandle, MaterialHandle};
199
200 #[test]
201 fn raw_parts_roundtrip_preserves_handle_identity() {
202 let handle = GeometryHandle::from_parts(42, 7);
203
204 assert_eq!(handle.raw_parts(), (42, 7));
205
206 let rebuilt = unsafe { GeometryHandle::from_raw_parts_unchecked(42, 7) };
207 assert_eq!(rebuilt, handle);
208 assert_eq!(rebuilt.raw_parts(), (42, 7));
209 }
210
211 #[test]
212 fn raw_parts_roundtrip_preserves_null_handles() {
213 let handle = MaterialHandle::default();
214 assert!(handle.is_null());
215 assert_eq!(handle.raw_parts(), (0, 0));
216
217 let rebuilt = unsafe { MaterialHandle::from_raw_parts_unchecked(0, 0) };
218 assert!(rebuilt.is_null());
219 assert_eq!(rebuilt.raw_parts(), (0, 0));
220 }
221}