macro_rules! define_id { ($name:ident) => { #[repr(transparent)] #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, serde::Serialize, serde::Deserialize)] pub struct $name(pub u32); impl $name { pub const NONE: $name = $name(u32::MAX); #[inline] pub const fn as_u32(self) -> u32 { self.0 } #[inline] pub fn is_none(self) -> bool { self == $name::NONE } } impl From for $name { #[inline] fn from(value: u32) -> Self { Self(value) } } impl From<$name> for u32 { #[inline] fn from(value: $name) -> Self { value.0 } } }; } define_id!(FileId); define_id!(NodeId); define_id!(NameId); define_id!(SymbolId); define_id!(TypeId); define_id!(ModuleId); define_id!(ProjectId); #[cfg(test)] mod tests { use super::*; use std::collections::HashMap; use std::mem::size_of; #[test] fn ids_are_repr_transparent_and_hashable() { assert_eq!(size_of::(), 4); assert_eq!(size_of::(), 4); assert_eq!(size_of::(), 4); assert_eq!(size_of::(), 4); assert_eq!(size_of::(), 4); assert_eq!(size_of::(), 4); assert_eq!(size_of::(), 4); // Hash/Eq usage let mut m: HashMap = HashMap::new(); m.insert(SymbolId(1), "one"); assert_eq!(m.get(&SymbolId(1)).copied(), Some("one")); } }