use crate::HasKey;
use alloc::borrow::Cow;
use alloc::vec::Vec;
use core::{
any::TypeId,
cell::Cell,
mem::{align_of, size_of},
};
#[derive(Clone, Copy, Debug)]
pub struct Field<'db> {
pub offset: usize,
pub name: Option<&'static str>,
#[cfg(feature = "attrs")]
pub attrs: &'db [Attr<'db>],
pub id: TypeId,
pub shape: DataShape<'db>,
}
#[derive(Clone, Copy, Debug)]
pub enum Attr<'db> {
Name(&'db str),
List(&'db str, &'db [&'db Attr<'db>]),
NameValue(&'db str, &'db RustPrimitive<'db>),
}
#[derive(Clone, Copy, Debug)]
pub enum RustPrimitive<'db> {
Str(&'db str),
ByteStr(&'db [u8]),
Byte(u8),
Char(char),
Int(i128),
Float(f64),
Bool(bool),
}
#[derive(Clone, Debug)]
pub struct ReflectedType<'db> {
pub id: TypeId,
pub name: &'static str,
pub layout: core::alloc::Layout,
#[cfg(feature = "attrs")]
pub attrs: Cow<'db, [Attr<'db>]>,
pub shape: DataShape<'db>,
}
#[derive(Clone, Copy, Debug)]
pub struct EnumArm<'db> {
pub label: &'static str,
pub variant_index: u16,
pub discriminant: u16,
#[cfg(feature = "attrs")]
pub attrs: &'db Attr<'db>,
pub decl_kind: DeclKind,
pub fields: ExpectArr<Field<'db>>,
}
#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum DeclKind {
Unit,
Tuple,
Struct,
Newtype,
}
#[derive(Clone, Copy, Debug)]
pub struct Expect<T>(core::marker::PhantomData<T>);
#[derive(Clone, Copy, Debug)]
pub struct ExpectArr<T> {
pub len: u16,
pub _marker: core::marker::PhantomData<[T]>,
}
pub enum Expectables<'db> {
Field(Field<'db>),
Shape(DataShape<'db>),
Shapes(Vec<DataShape<'db>>),
EnumArms(Vec<EnumArm<'db>>),
}
#[derive(Clone, Copy, Debug)]
pub enum DataShape<'db> {
Leaf(TypeId),
Builtin(RustBuiltin),
FixedArray {
shape: Expect<DataShape<'db>>,
len: usize,
stride: usize,
},
Slice(Expect<DataShape<'db>>),
Ref(Expect<DataShape<'db>>, RefKind),
Tuple(ExpectArr<Field<'db>>),
Enum(&'static [&'static str], ExpectArr<EnumArm<'db>>),
Struct(DeclKind, &'static [&'static str], ExpectArr<Field<'db>>),
}
#[derive(Clone, Copy, Debug)]
pub enum RefKind {
Fat,
Thin,
}
#[derive(Clone, Copy, Debug)]
pub enum RustBuiltin {
U8,
I8,
U16,
I16,
U32,
I32,
U64,
I64,
U128,
I128,
F32,
F64,
BOOLIN,
CHAR,
}
#[derive(Clone)]
pub struct FieldsCursor<'db> {
pub(crate) cursor: Cell<*mut u8>,
pub(crate) _marker: core::marker::PhantomData<&'db ()>,
}
#[allow(missing_docs)]
#[allow(clippy::clippy::missing_safety_doc)]
impl<'db> FieldsCursor<'db> {
unsafe fn advance<T>(&self) {
self.cursor
.set(self.cursor.get().wrapping_add(size_of::<T>()));
}
unsafe fn align<T>(&self) {
self.cursor.set(
self.cursor
.get()
.wrapping_add(self.cursor.get().align_offset(align_of::<T>())),
);
}
unsafe fn verify_type<T: 'static>(&self) {
#[cfg(debug_assertions)]
{
let actual_type = self.cursor.get().cast::<TypeId>().read_unaligned();
let expected_type = TypeId::of::<T>();
debug_assert_eq!(actual_type, expected_type);
self.advance::<TypeId>();
}
}
unsafe fn write_type<T: 'static>(&self) {
self.cursor
.get()
.cast::<TypeId>()
.write_unaligned(TypeId::of::<T>());
self.advance::<TypeId>();
}
unsafe fn write_any<T: HasKey>(&self, val: T) {
self.write_type::<T::Key>();
self.align::<T>();
self.cursor.get().cast::<T>().write(val);
}
pub unsafe fn array<T: HasKey>(&self, len: &ExpectArr<T>) -> &'db [T] {
self.verify_type::<T::Key>();
self.align::<T>();
let slice = core::slice::from_raw_parts(self.cursor.get().cast(), len.len as usize);
let total_advancement = size_of::<T>() * len.len as usize;
self.cursor
.set(self.cursor.get().wrapping_add(total_advancement));
slice
}
unsafe fn scan_any<T: HasKey>(&self, advance: bool) -> &'db T {
let c = self.cursor.get();
self.verify_type::<T::Key>();
self.align::<T>();
let v = &*(self.cursor.get() as *const T);
if !advance {
self.cursor.set(c);
}
v
}
unsafe fn read_any<T: HasKey>(&self) -> &'db T {
let v = self.scan_any(true);
self.cursor
.set(self.cursor.get().wrapping_add(size_of::<T>()));
v
}
pub unsafe fn write_enum_arm(&self, arm: EnumArm<'db>) {
self.write_any(arm)
}
pub unsafe fn write_field(&self, field: Field<'db>) {
self.write_any(field);
}
pub unsafe fn write_shape(&self, shape: DataShape<'db>) {
self.write_any(shape);
}
pub unsafe fn shape(&self, _: &Expect<DataShape<'db>>) -> &'db DataShape<'db> {
self.read_any()
}
pub unsafe fn field(&self, _: &Expect<Field<'db>>) -> &'db Field<'db> {
self.read_any()
}
pub unsafe fn peek_first_field(&self, _: &ExpectArr<Field<'db>>) -> &'db Field<'db> {
self.scan_any(false)
}
}