use std::{
any::{Any, TypeId},
fmt::{Debug, Display},
hash::Hash,
marker::PhantomData,
};
use slotmap::{Key, new_key_type};
new_key_type! {
pub struct ItemId;
}
#[repr(transparent)]
pub struct PropId<T: 'static>(pub(crate) ItemId, PhantomData<T>);
impl<T> PropId<T> {
pub(crate) fn new(id: ItemId) -> Self {
Self(id, PhantomData)
}
pub fn value(&self) -> u64 {
self.0.data().as_ffi()
}
pub fn erase_type(&self) -> PropId<()> {
PropId(self.0, PhantomData)
}
}
impl<T> Display for PropId<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:#x}", self.value())
}
}
impl<T> Debug for PropId<T> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "PropId({:#})", self)
}
}
impl<T> Clone for PropId<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for PropId<T> {}
impl<T> PartialEq for PropId<T> {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl<T> PartialOrd for PropId<T> {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl<T> Hash for PropId<T> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
impl<T> Eq for PropId<T> {}
impl<T> Ord for PropId<T> {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.0.cmp(&other.0)
}
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SlabId(pub(crate) u64);
impl SlabId {
pub fn value(&self) -> u64 {
self.0
}
}
impl Display for SlabId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if f.alternate() { write!(f, "{:#04x}", self.0) } else { write!(f, "{:04x}", self.0) }
}
}
impl Debug for SlabId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "SlabId({:#})", self)
}
}
#[derive(Debug)]
pub enum PropValue {
Unit(()),
Bool(bool),
Char(char),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
USize(usize),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
ISize(isize),
F32(f32),
F64(f64),
Str(String),
Any(Box<dyn Any>),
}
impl PropValue {
pub fn new<T: 'static>(value: T) -> Self {
let id = TypeId::of::<T>();
macro_rules! try_cast {
($ty:ty, $var:ident) => {
if id == TypeId::of::<$ty>() {
unsafe {
let casted = std::ptr::read(&value as *const T as *const $ty);
std::mem::forget(value);
return PropValue::$var(casted);
}
}
};
}
try_cast!((), Unit);
try_cast!(bool, Bool);
try_cast!(char, Char);
try_cast!(u8, U8);
try_cast!(u16, U16);
try_cast!(u32, U32);
try_cast!(u64, U64);
try_cast!(usize, USize);
try_cast!(i8, I8);
try_cast!(i16, I16);
try_cast!(i32, I32);
try_cast!(i64, I64);
try_cast!(isize, ISize);
try_cast!(f32, F32);
try_cast!(f64, F64);
try_cast!(String, Str);
PropValue::Any(Box::new(value))
}
pub fn get<T: 'static>(&self) -> &T {
let id = TypeId::of::<T>();
macro_rules! try_get {
($ty:ty, $var:ident) => {
if id == TypeId::of::<$ty>() {
match self {
PropValue::$var(val) => {
return unsafe { &*(val as *const $ty as *const T) };
}
_ => unreachable!(),
}
}
};
}
try_get!((), Unit);
try_get!(bool, Bool);
try_get!(char, Char);
try_get!(u8, U8);
try_get!(u16, U16);
try_get!(u32, U32);
try_get!(u64, U64);
try_get!(usize, USize);
try_get!(i8, I8);
try_get!(i16, I16);
try_get!(i32, I32);
try_get!(i64, I64);
try_get!(isize, ISize);
try_get!(f32, F32);
try_get!(f64, F64);
try_get!(String, Str);
match self {
PropValue::Any(val) => val.downcast_ref::<T>().unwrap(),
_ => unreachable!(),
}
}
pub fn get_mut<T: 'static>(&mut self) -> &mut T {
let id = TypeId::of::<T>();
macro_rules! try_get_mut {
($ty:ty, $var:ident) => {
if id == TypeId::of::<$ty>() {
match self {
PropValue::$var(val) => {
return unsafe { &mut *(val as *mut $ty as *mut T) };
}
_ => unreachable!(),
}
}
};
}
try_get_mut!((), Unit);
try_get_mut!(bool, Bool);
try_get_mut!(char, Char);
try_get_mut!(u8, U8);
try_get_mut!(u16, U16);
try_get_mut!(u32, U32);
try_get_mut!(u64, U64);
try_get_mut!(usize, USize);
try_get_mut!(i8, I8);
try_get_mut!(i16, I16);
try_get_mut!(i32, I32);
try_get_mut!(i64, I64);
try_get_mut!(isize, ISize);
try_get_mut!(f32, F32);
try_get_mut!(f64, F64);
try_get_mut!(String, Str);
match self {
PropValue::Any(val) => val.downcast_mut::<T>().unwrap(),
_ => unreachable!(),
}
}
}