use std::{any::TypeId, marker::PhantomData};
pub trait Model: 'static {
fn model() -> TypeModel;
}
pub type ModelName = &'static str;
#[derive(Debug)]
pub struct StructModel {
pub name: ModelName,
pub type_id: TypeId,
pub total_len: usize,
pub fields: Vec<FieldModel>,
}
pub struct StructModelBuilder(StructModel);
#[derive(Debug)]
pub struct FieldModel {
pub name: ModelName,
pub type_model: TypeModel,
pub offset: usize,
pub size: usize,
}
#[derive(Debug)]
pub enum TypeModel {
Empty,
Struct(Box<StructModel>),
Array(Box<(TypeModel, usize)>),
Primitive(ModelName),
}
impl StructModel {
pub fn new_opaque(name: ModelName, type_id: TypeId, size: usize) -> Self {
Self {
name,
type_id,
total_len: size,
fields: Vec::new(),
}
}
pub fn new_builder(name: ModelName, type_id: TypeId) -> StructModelBuilder {
StructModelBuilder(StructModel {
name,
type_id,
total_len: 0,
fields: Vec::new(),
})
}
}
impl StructModelBuilder {
pub fn add_field(
&mut self,
name: ModelName,
type_model: TypeModel,
offset: usize,
size: usize,
) -> &mut Self {
if offset < self.0.total_len {
self.0.total_len = offset;
}
self.0.fields.push(FieldModel {
name,
offset,
type_model,
size,
});
self.0.total_len += size;
self
}
pub fn set_total_len(&mut self, size: usize) -> &mut Self {
assert!(size >= self.0.total_len);
self.0.total_len = size;
self
}
pub fn build(self) -> StructModel {
self.0
}
}
macro_rules! builtin_impl {
($($types:tt ) *) => {
$(
impl Model for $types {
fn model() -> TypeModel {
TypeModel::Primitive(stringify!($types))
}
}
)*
};
}
builtin_impl!(bool u8 i8 u64 i64 f64 u32 i32 f32 u16 i16);
impl<T, const N: usize> Model for [T; N]
where
T: Model,
{
fn model() -> TypeModel {
TypeModel::Array(Box::new((T::model(), N)))
}
}
impl<T, const N: usize> Model for Box<[T; N]>
where
T: Model,
{
fn model() -> TypeModel {
TypeModel::Array(Box::new((T::model(), N)))
}
}
impl<T: 'static> Model for PhantomData<T> {
fn model() -> TypeModel {
TypeModel::Empty
}
}