use core::fmt;
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize, Serializer, Deserializer};
#[repr(transparent)]
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Slot(u32);
impl Slot {
pub const NONE: Slot = Slot(u32::MAX);
#[inline]
pub fn new(slot: u32) -> Self {
debug_assert!(slot != u32::MAX, "slot value u32::MAX is reserved for NONE");
Slot(slot)
}
#[inline]
pub fn from_usize(slot: usize) -> Self {
if slot < u32::MAX as usize {
Slot(slot as u32)
} else {
Slot::NONE
}
}
#[inline]
pub fn get(self) -> Option<usize> {
if self.0 != u32::MAX {
Some(self.0 as usize)
} else {
None
}
}
#[inline]
pub fn as_raw(self) -> u32 {
self.0
}
#[inline]
pub fn from_raw(raw: u32) -> Self {
Slot(raw)
}
#[inline]
pub fn is_some(self) -> bool {
self.0 != u32::MAX
}
#[inline]
pub fn is_none(self) -> bool {
self.0 == u32::MAX
}
#[inline]
pub fn map<U, F: FnOnce(usize) -> U>(self, f: F) -> Option<U> {
self.get().map(f)
}
#[inline]
pub fn unwrap_or(self, default: usize) -> usize {
self.get().unwrap_or(default)
}
#[inline]
pub fn unwrap(self) -> usize {
self.get().expect("called unwrap() on Slot::NONE")
}
}
impl Default for Slot {
#[inline]
fn default() -> Self {
Slot::NONE
}
}
impl fmt::Debug for Slot {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.get() {
Some(idx) => write!(f, "Slot({})", idx),
None => write!(f, "Slot::NONE"),
}
}
}
impl fmt::Display for Slot {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.get() {
Some(idx) => write!(f, "{}", idx),
None => write!(f, "-"),
}
}
}
impl From<u32> for Slot {
#[inline]
fn from(slot: u32) -> Self {
Slot::new(slot)
}
}
impl From<usize> for Slot {
#[inline]
fn from(slot: usize) -> Self {
Slot::from_usize(slot)
}
}
#[cfg(feature = "serde")]
impl Serialize for Slot {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.0.serialize(serializer)
}
}
#[cfg(feature = "serde")]
impl<'de> Deserialize<'de> for Slot {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
u32::deserialize(deserializer).map(Slot::from_raw)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_slot_size() {
assert_eq!(std::mem::size_of::<Slot>(), 4);
}
#[test]
fn test_new_and_get() {
let slot = Slot::new(42);
assert_eq!(slot.get(), Some(42));
assert!(slot.is_some());
assert!(!slot.is_none());
}
#[test]
fn test_none() {
let slot = Slot::NONE;
assert_eq!(slot.get(), None);
assert!(!slot.is_some());
assert!(slot.is_none());
}
#[test]
fn test_default_is_none() {
let slot: Slot = Default::default();
assert!(slot.is_none());
}
#[test]
fn test_from_usize() {
assert_eq!(Slot::from_usize(0).get(), Some(0));
assert_eq!(Slot::from_usize(100).get(), Some(100));
assert_eq!(Slot::from_usize(u32::MAX as usize - 1).get(), Some(u32::MAX as usize - 1));
assert!(Slot::from_usize(u32::MAX as usize).is_none());
assert!(Slot::from_usize(usize::MAX).is_none());
}
#[test]
fn test_as_raw_and_from_raw() {
let slot = Slot::new(42);
assert_eq!(slot.as_raw(), 42);
assert_eq!(Slot::from_raw(42).get(), Some(42));
let none = Slot::NONE;
assert_eq!(none.as_raw(), u32::MAX);
assert!(Slot::from_raw(u32::MAX).is_none());
}
#[test]
fn test_map() {
let slot = Slot::new(10);
assert_eq!(slot.map(|x| x * 2), Some(20));
let none = Slot::NONE;
assert_eq!(none.map(|x| x * 2), None);
}
#[test]
fn test_unwrap_or() {
assert_eq!(Slot::new(42).unwrap_or(0), 42);
assert_eq!(Slot::NONE.unwrap_or(99), 99);
}
#[test]
fn test_unwrap() {
assert_eq!(Slot::new(42).unwrap(), 42);
}
#[test]
#[should_panic(expected = "called unwrap() on Slot::NONE")]
fn test_unwrap_none_panics() {
Slot::NONE.unwrap();
}
#[test]
fn test_debug_format() {
assert_eq!(format!("{:?}", Slot::new(42)), "Slot(42)");
assert_eq!(format!("{:?}", Slot::NONE), "Slot::NONE");
}
#[test]
fn test_display_format() {
assert_eq!(format!("{}", Slot::new(42)), "42");
assert_eq!(format!("{}", Slot::NONE), "-");
}
#[test]
fn test_equality() {
assert_eq!(Slot::new(1), Slot::new(1));
assert_ne!(Slot::new(1), Slot::new(2));
assert_eq!(Slot::NONE, Slot::NONE);
assert_ne!(Slot::new(0), Slot::NONE);
}
#[test]
fn test_hash() {
use std::collections::HashSet;
let mut set = HashSet::new();
set.insert(Slot::new(1));
set.insert(Slot::new(2));
set.insert(Slot::NONE);
assert!(set.contains(&Slot::new(1)));
assert!(set.contains(&Slot::new(2)));
assert!(set.contains(&Slot::NONE));
assert!(!set.contains(&Slot::new(3)));
}
#[test]
fn test_from_u32() {
let slot: Slot = 42u32.into();
assert_eq!(slot.get(), Some(42));
}
#[test]
fn test_from_usize_trait() {
let slot: Slot = 42usize.into();
assert_eq!(slot.get(), Some(42));
}
#[test]
fn test_zero_is_valid() {
let slot = Slot::new(0);
assert_eq!(slot.get(), Some(0));
assert!(slot.is_some());
}
#[test]
fn test_max_valid_slot() {
let slot = Slot::new(u32::MAX - 1);
assert_eq!(slot.get(), Some((u32::MAX - 1) as usize));
assert!(slot.is_some());
}
#[test]
fn test_question_mark_operator() {
fn use_slot(slot: Slot) -> Option<usize> {
let idx = slot.get()?;
Some(idx + 1)
}
assert_eq!(use_slot(Slot::new(10)), Some(11));
assert_eq!(use_slot(Slot::NONE), None);
}
#[cfg(feature = "serde")]
#[test]
fn test_serde_roundtrip() {
let slot = Slot::new(42);
let json = serde_json::to_string(&slot).unwrap();
assert_eq!(json, "42");
let back: Slot = serde_json::from_str(&json).unwrap();
assert_eq!(back, slot);
let none = Slot::NONE;
let json = serde_json::to_string(&none).unwrap();
assert_eq!(json, format!("{}", u32::MAX));
let back: Slot = serde_json::from_str(&json).unwrap();
assert_eq!(back, none);
}
}