use crate::resources::id::ResourceId32;
use std::fmt::{Display, Formatter};
use std::hash::Hash;
#[allow(dead_code)]
pub(crate) trait HandleType: Copy + Clone + PartialEq + Eq + Hash + Default {
fn from_raw(raw: ResourceId32) -> Self;
fn to_raw(self) -> ResourceId32;
fn is_null(self) -> bool {
let raw = self.to_raw();
raw.index() == 0 && raw.generation() == 0
}
}
macro_rules! define_handle {
(
$(#[$meta:meta])*
$name:ident
) => {
$(#[$meta])*
#[repr(transparent)]
#[derive(Default, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct $name(ResourceId32);
#[allow(dead_code)]
impl $name {
#[must_use]
pub fn is_null(self) -> bool {
self.0.index() == 0 && self.0.generation() == 0
}
pub(crate) fn from_parts(index: u32, generation: u16) -> Self {
Self(ResourceId32::new(index, generation))
}
pub(crate) fn index(self) -> u32 {
self.0.index()
}
pub(crate) fn generation(self) -> u16 {
self.0.generation()
}
#[must_use]
pub fn raw_parts(self) -> (u32, u16) {
self.to_raw_parts()
}
#[must_use]
pub unsafe fn from_raw_parts_unchecked(index: u32, generation: u16) -> Self {
Self::from_raw_parts(index, generation)
}
pub(crate) fn from_raw_parts(index: u32, generation: u16) -> Self {
Self(ResourceId32::new(index, generation))
}
pub(crate) fn to_raw_parts(self) -> (u32, u16) {
(self.0.index(), self.0.generation())
}
pub(crate) fn to_raw(self) -> ResourceId32 {
self.0
}
pub(crate) fn from_raw(raw: ResourceId32) -> Self {
Self(raw)
}
}
impl Display for $name {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", stringify!($name))
}
}
impl std::fmt::Debug for $name {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}(index={}, generation={})", stringify!($name), self.0.index(), self.0.generation())
}
}
impl HandleType for $name {
fn from_raw(raw: ResourceId32) -> Self {
Self(raw)
}
fn to_raw(self) -> ResourceId32 {
self.0
}
}
};
}
define_handle! {
GeometryHandle
}
define_handle! {
GeometryTemplateHandle
}
define_handle! {
SemanticHandle
}
define_handle! {
MaterialHandle
}
define_handle! {
TextureHandle
}
define_handle! {
CityObjectHandle
}
#[inline]
pub(crate) fn cast_handle_slice<H: HandleType>(raw: &[ResourceId32]) -> &[H] {
const {
assert!(std::mem::size_of::<H>() == std::mem::size_of::<ResourceId32>());
assert!(std::mem::align_of::<H>() == std::mem::align_of::<ResourceId32>());
}
unsafe { std::slice::from_raw_parts(raw.as_ptr().cast::<H>(), raw.len()) }
}
#[inline]
pub(crate) fn cast_option_handle_slice<H: HandleType>(
raw: &[Option<ResourceId32>],
) -> &[Option<H>] {
const {
assert!(std::mem::size_of::<Option<H>>() == std::mem::size_of::<Option<ResourceId32>>());
assert!(std::mem::align_of::<Option<H>>() == std::mem::align_of::<Option<ResourceId32>>());
}
unsafe { std::slice::from_raw_parts(raw.as_ptr().cast::<Option<H>>(), raw.len()) }
}
#[cfg(test)]
mod tests {
use super::{GeometryHandle, MaterialHandle};
#[test]
fn raw_parts_roundtrip_preserves_handle_identity() {
let handle = GeometryHandle::from_parts(42, 7);
assert_eq!(handle.raw_parts(), (42, 7));
let rebuilt = unsafe { GeometryHandle::from_raw_parts_unchecked(42, 7) };
assert_eq!(rebuilt, handle);
assert_eq!(rebuilt.raw_parts(), (42, 7));
}
#[test]
fn raw_parts_roundtrip_preserves_null_handles() {
let handle = MaterialHandle::default();
assert!(handle.is_null());
assert_eq!(handle.raw_parts(), (0, 0));
let rebuilt = unsafe { MaterialHandle::from_raw_parts_unchecked(0, 0) };
assert!(rebuilt.is_null());
assert_eq!(rebuilt.raw_parts(), (0, 0));
}
}