use core::alloc::Layout;
use core::any::{self, TypeId};
use core::{mem, panic};
use crate::model::Model;
use crate::Reflect;
#[derive(Debug, Clone, Copy)]
pub struct ConstTypeId(fn () -> TypeId);
impl ConstTypeId {
#[inline]
pub const fn new<T: ?Sized + 'static>() -> Self {
Self(|| TypeId::of::<T>())
}
#[inline]
pub fn get(&self) -> any::TypeId {
(self.0)()
}
}
#[derive(Debug)]
pub struct Field<'ty> {
pub name: &'ty str,
pub model: &'ty Model<'ty>,
pub offset: usize,
}
impl<'ty> Field<'ty> {
pub const fn new<T: ?Sized + Reflect>(name: &'ty str, offset: usize) -> Self {
Self { name, model: T::MODEL, offset }
}
}
#[derive(Debug)]
pub struct StructType<'ty> {
pub fields: &'ty [Field<'ty>],
}
#[derive(Debug)]
pub enum PrimitiveType {
Bool,
Char,
U8,
U16,
U32,
U64,
U128,
I8,
I16,
I32,
I64,
I128,
F32,
F64,
}
#[derive(Debug)]
pub enum TypeKind<'ty> {
Primitive(PrimitiveType),
Str,
Struct(StructType<'ty>),
Slice(&'ty Type<'ty>),
Array {
of: &'ty Type<'ty>,
len: usize,
},
Ref {
to: &'ty Model<'ty>,
wide: bool,
},
Ptr {
is_const: bool,
to: &'ty Model<'ty>,
wide: bool,
},
}
#[derive(Debug,Clone,Copy)]
pub enum LayoutKind {
Sized(Layout),
Unsized,
}
impl LayoutKind {
pub const fn as_sized(&self) -> Option<Layout> {
match self {
LayoutKind::Sized(layout) => Some(*layout),
LayoutKind::Unsized => None,
}
}
}
#[derive(Debug)]
pub struct Type<'ty> {
pub kind: TypeKind<'ty>,
pub layout: LayoutKind,
pub id: ConstTypeId,
}
impl<'ty> Type<'ty> {
pub const fn ptr<T: Reflect + ?Sized + 'static>() -> Self {
let wide = mem::size_of::<*const T>() == mem::size_of::<*const [u8]>();
let kind = TypeKind::Ptr { is_const: true, to: T::MODEL, wide };
TypeBuilder::new::<*const T>().kind(kind).build()
}
pub const fn ptr_mut<T: Reflect + ?Sized + 'static>() -> Self {
let wide = mem::size_of::<*mut T>() == mem::size_of::<*mut [u8]>();
let kind = TypeKind::Ptr { is_const: false, to: T::MODEL, wide };
TypeBuilder::new::<*mut T>().kind(kind).build()
}
pub const fn reference<T: ?Sized + Reflect + 'static>() -> Self {
let wide = mem::size_of::<&T>() == mem::size_of::<&[u8]>();
let kind = TypeKind::Ref { to: T::MODEL, wide };
TypeBuilder::new::<&T>().kind(kind).build()
}
pub const fn primitive<T: 'static>(primitive: PrimitiveType) -> Self {
TypeBuilder::new::<T>()
.kind(TypeKind::Primitive(primitive))
.build()
}
pub const fn array<T: Reflect + 'static, const N: usize>() -> Self {
let layout = match Layout::array::<T>(N) {
Ok(l) => l,
Err(_) => panic!()
};
Self {
kind: TypeKind::Array {
of: T::MODEL.ty,
len: N,
},
layout: LayoutKind::Sized(layout),
id: ConstTypeId::new::<[T; N]>(),
}
}
pub const fn slice<T: Reflect + 'static>() -> Self {
Self {
kind: TypeKind::Slice(T::MODEL.ty),
layout: LayoutKind::Unsized,
id: ConstTypeId::new::<[T]>(),
}
}
}
#[macro_export]
macro_rules! struct_type {
($( $n:path : $t:ty ),* $(,)?) => {
&const { $crate::types::TypeBuilder::new::<Self>()
.set_struct(&const {[
$(
$crate::types::Field::new::<$t>(stringify!($n), offset_of!(Self, $n)),
)*
]})
.build()
}
};
}
pub struct TypeBuilder<'ty> {
kind: Option<TypeKind<'ty>>,
layout: LayoutKind,
id: ConstTypeId,
}
impl<'ty> TypeBuilder<'ty> {
pub const fn new<T: 'static>() -> Self {
Self { kind: None, layout: LayoutKind::Sized(Layout::new::<T>()), id: ConstTypeId::new::<T>() }
}
pub const fn new_unsized<T: ?Sized + 'static>() -> Self {
Self { kind: None, layout: LayoutKind::Unsized, id: ConstTypeId::new::<T>() }
}
pub const fn kind(&mut self, kind: TypeKind<'ty>) -> &mut Self {
self.kind = Some(kind);
self
}
pub const fn set_struct(&mut self, fields: &'ty [Field<'ty>]) -> &mut Self {
self.kind = Some(TypeKind::Struct(StructType { fields }));
self
}
pub const fn build(&mut self) -> Type<'ty> {
Type { kind: self.kind.take().unwrap(), layout: self.layout, id: self.id }
}
}