use crate::{str::Str, StableLike};
use sha2_const_stable::Sha256;
type NextField = StableLike<Option<&'static FieldReport>, usize>;
#[crate::stabby]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct TypeReport {
pub name: Str<'static>,
pub module: Str<'static>,
pub fields: NextField,
pub tyty: TyTy,
pub version: u32,
}
impl core::fmt::Display for TypeReport {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let Self {
name,
module,
version,
tyty,
..
} = self;
write!(f, "{tyty:?} {module} :: {name} (version{version}) {{")?;
for FieldReport { name, ty, .. } in self.fields() {
write!(f, "{name}: {ty}, ")?
}
write!(f, "}}")
}
}
impl core::hash::Hash for TypeReport {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.module.hash(state);
for field in self.fields() {
field.hash(state);
}
self.version.hash(state);
self.tyty.hash(state);
}
}
impl TypeReport {
pub fn is_compatible(&self, other: &Self) -> bool {
self.name == other.name
&& self.module == other.module
&& self.version == other.version
&& self.tyty == other.tyty
&& self
.fields()
.zip(other.fields())
.all(|(s, o)| s.name == o.name && s.ty.is_compatible(o.ty))
}
}
#[crate::stabby]
#[repr(u8)]
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub enum TyTy {
Struct,
Enum(Str<'static>),
Union,
}
#[crate::stabby]
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct FieldReport {
pub name: Str<'static>,
pub ty: &'static TypeReport,
pub next_field: NextField,
}
impl core::hash::Hash for FieldReport {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.ty.hash(state);
}
}
impl TypeReport {
pub const fn fields(&self) -> Fields {
Fields(self.fields.value)
}
}
#[crate::stabby]
pub struct Fields(Option<&'static FieldReport>);
impl Fields {
pub const fn next_const(self) -> (Self, Option<&'static FieldReport>) {
match self.0 {
Some(field) => (Self(*field.next_field.as_ref()), Some(field)),
None => (self, None),
}
}
}
impl Iterator for Fields {
type Item = &'static FieldReport;
fn next(&mut self) -> Option<Self::Item> {
let field = self.0.take()?;
self.0 = field.next_field.value;
Some(field)
}
}
const fn hash_report(mut hash: Sha256, report: &TypeReport) -> Sha256 {
hash = hash
.update(report.module.as_str().as_bytes())
.update(report.name.as_str().as_bytes());
hash = match report.tyty {
crate::report::TyTy::Struct => hash.update(&[0]),
crate::report::TyTy::Union => hash.update(&[1]),
crate::report::TyTy::Enum(s) => hash.update(s.as_str().as_bytes()),
};
let mut fields = report.fields();
while let (new, Some(next)) = fields.next_const() {
fields = new;
hash = hash_report(hash.update(next.name.as_str().as_bytes()), next.ty)
}
hash
}
const ARCH_INFO: [u8; 8] = [
0,
core::mem::size_of::<usize>() as u8,
core::mem::align_of::<usize>() as u8,
core::mem::size_of::<&()>() as u8,
core::mem::align_of::<&()>() as u8,
core::mem::align_of::<u128>() as u8,
unsafe { core::mem::transmute::<[u8; 2], u16>([0, 1]) } as u8,
0,
];
pub const fn gen_id(report: &TypeReport) -> u64 {
let [hash @ .., _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _] =
hash_report(Sha256::new(), report).finalize();
u64::from_le_bytes(hash) ^ u64::from_le_bytes(ARCH_INFO)
}