use core::fmt;
use core::num::NonZeroU32;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Symbol(NonZeroU32);
impl Symbol {
#[inline]
#[must_use]
pub fn from_u32(id: u32) -> Option<Self> {
NonZeroU32::new(id).map(Self)
}
#[inline]
pub(crate) fn from_raw(id: u32) -> Self {
Self(NonZeroU32::new(id).unwrap_or(NonZeroU32::MIN))
}
#[inline]
pub(crate) fn index(self) -> usize {
self.0.get() as usize - 1
}
#[inline]
#[must_use]
pub fn as_u32(self) -> u32 {
self.0.get()
}
}
impl fmt::Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Symbol({})", self.0.get())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_raw_roundtrips_through_as_u32() {
for id in [1u32, 2, 7, 1000, u32::MAX] {
assert_eq!(Symbol::from_raw(id).as_u32(), id);
}
}
#[test]
fn test_index_is_zero_based() {
assert_eq!(Symbol::from_raw(1).index(), 0);
assert_eq!(Symbol::from_raw(42).index(), 41);
}
#[test]
fn test_symbol_is_four_bytes() {
assert_eq!(core::mem::size_of::<Symbol>(), 4);
}
#[test]
fn test_option_symbol_is_niche_optimized() {
assert_eq!(core::mem::size_of::<Option<Symbol>>(), 4);
}
#[test]
fn test_equal_ids_compare_equal() {
assert_eq!(Symbol::from_raw(5), Symbol::from_raw(5));
assert_ne!(Symbol::from_raw(5), Symbol::from_raw(6));
}
#[test]
fn test_debug_shows_id() {
extern crate alloc;
assert_eq!(alloc::format!("{:?}", Symbol::from_raw(9)), "Symbol(9)");
}
}