use std::alloc::Layout;
use std::cmp::{Ord, Ordering};
use std::fmt::{Debug, Formatter};
use std::sync::{RwLock, Arc};
use once_cell::sync::{Lazy, OnceCell};
use crate::EntityID;
use std::fmt;
use std::any::type_name;
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ComponentTypeID(usize);
struct ComponentRegistry {
component_types: Vec<Arc<ComponentRegistration>>,
}
static mut COMPONENT_REGISTRY: Lazy<RwLock<ComponentRegistry>> = Lazy::new(|| {
RwLock::new(ComponentRegistry {
component_types: vec![
Arc::new(ComponentRegistration::new::<EntityID>(ComponentTypeID(0))),
],
})
});
impl ComponentTypeID {
pub(crate) fn new(inner: usize) -> ComponentTypeID {
ComponentTypeID(inner)
}
pub fn register<T: Component>() -> ComponentTypeID {
unsafe {
let mut r = COMPONENT_REGISTRY.write().unwrap();
let id = ComponentTypeID(r.component_types.len());
r.component_types.push(Arc::new(ComponentRegistration::new::<T>(id)));
id
}
}
fn safe_registration(&self) -> Option<Arc<ComponentRegistration>> {
unsafe {
let r = COMPONENT_REGISTRY.read().unwrap();
r.component_types.get(self.0).cloned()
}
}
pub fn registration(&self) -> Arc<ComponentRegistration> {
self.safe_registration().unwrap()
}
pub fn id(&self) -> usize {
self.0
}
pub fn layout(&self) -> Layout {
self.registration().layout()
}
pub fn name(&self) -> &'static str {
self.registration().name()
}
}
impl Debug for ComponentTypeID {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self.safe_registration() {
Some(reg) => write!(f, "{}", reg.name()),
None => write!(f, "ComponentTypeID(#{} missing)", self.0),
}
}
}
pub struct AutoComponentTypeID(OnceCell<ComponentTypeID>);
impl AutoComponentTypeID {
pub const fn new() -> AutoComponentTypeID {
AutoComponentTypeID(OnceCell::new())
}
pub fn get<T: Component>(&self) -> ComponentTypeID {
self.0.get_or_init(ComponentTypeID::register::<T>).clone()
}
}
pub unsafe trait Component: Debug + Default + Copy {
fn type_id() -> ComponentTypeID;
fn layout() -> Layout;
}
#[derive(Clone, Copy)]
pub struct ComponentRegistration {
type_id: ComponentTypeID,
layout: Layout,
set_default: fn(&mut [u8]),
name: &'static str,
}
impl ComponentRegistration {
pub fn new<T: Component>(type_id: ComponentTypeID) -> ComponentRegistration {
fn default<T: Component>(ptr: &mut [u8]) {
assert_eq!(ptr.len(), std::mem::size_of::<T>());
let ptr: &mut T = unsafe { std::mem::transmute(ptr.as_ptr()) };
let d = T::default();
*ptr = d;
}
ComponentRegistration {
type_id,
layout: T::layout(),
set_default: default::<T>,
name: type_name::<T>(),
}
}
pub fn type_id(&self) -> ComponentTypeID {
self.type_id
}
pub fn layout(&self) -> Layout {
self.layout
}
pub fn name(&self) -> &'static str {
self.name
}
pub fn set_default(&self, ptr: &mut [u8]) {
(self.set_default)(ptr)
}
}
impl PartialEq for ComponentRegistration {
fn eq(&self, other: &ComponentRegistration) -> bool {
self.type_id.eq(&other.type_id)
}
}
impl Eq for ComponentRegistration {}
impl PartialOrd for ComponentRegistration {
fn partial_cmp(&self, other: &ComponentRegistration) -> Option<Ordering> {
self.type_id.partial_cmp(&other.type_id)
}
}
impl Ord for ComponentRegistration {
fn cmp(&self, other: &ComponentRegistration) -> Ordering {
self.type_id.cmp(&other.type_id)
}
}
impl Debug for ComponentRegistration {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<ComponentRegistration {:?}>", self.type_id.id())
}
}
#[macro_export]
macro_rules! component {
($i:ident) => {
const _: () = {
static INIT_TYPE: $crate::component::AutoComponentTypeID = $crate::component::AutoComponentTypeID::new();
unsafe impl $crate::component::Component for $i {
fn type_id() -> $crate::component::ComponentTypeID {
INIT_TYPE.get::<$i>()
}
fn layout() -> ::core::alloc::Layout {
::core::alloc::Layout::new::<$i>()
}
}
()
};
};
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_uniqueness() {
#[derive(Debug, Clone, Copy, Default)]
struct A;
#[derive(Debug, Clone, Copy, Default)]
struct B;
component!(A);
component!(B);
assert_ne!(ComponentTypeID(0), A::type_id());
assert_ne!(ComponentTypeID(0), B::type_id());
assert_ne!(A::type_id(), B::type_id());
}
#[test]
fn test_default() {
#[derive(Debug, Clone, Copy)]
struct A(u8);
component!(A);
impl Default for A {
fn default() -> A {
A(42)
}
}
let component_type = ComponentRegistration::new::<A>(ComponentTypeID(12));
assert_eq!(component_type.type_id(), A::type_id());
assert_eq!(component_type.layout(), Layout::new::<A>());
let raw = &mut [0];
component_type.set_default(raw);
assert_eq!(raw[0], 42);
}
}