use std::{
convert::TryInto,
ffi::CStr,
fmt::Debug,
fmt::{self, Formatter},
os::raw::c_char,
str,
};
use crate::{type_id::TypeId, Guid, StructDefinition};
#[repr(C)]
pub struct TypeDefinition<'a> {
pub name: *const c_char,
pub(crate) size_in_bits: u32,
pub(crate) alignment: u8,
pub data: TypeDefinitionData<'a>,
}
impl<'a> Debug for TypeDefinition<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("TypeDefinition")
.field("name", &self.name())
.field("size_in_bits", &self.size_in_bits)
.field("alignment", &self.alignment)
.field("data", &self.data)
.finish()
}
}
#[cfg(feature = "serde")]
impl<'a> serde::Serialize for TypeDefinition<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use serde::ser::SerializeStruct;
let mut s = serializer.serialize_struct("TypeDefinition", 4)?;
s.serialize_field("name", self.name())?;
s.serialize_field("size_in_bits", &self.size_in_bits)?;
s.serialize_field("alignment", &self.alignment)?;
s.serialize_field("data", &self.data)?;
s.end()
}
}
#[repr(u8)]
#[derive(Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum TypeDefinitionData<'a> {
Struct(StructDefinition<'a>),
}
impl<'a> TypeDefinition<'a> {
pub fn is_instance_of(&self, type_id: &TypeId<'a>) -> bool {
match (&self.data, type_id) {
(TypeDefinitionData::Struct(s), TypeId::Concrete(guid)) => &s.guid == guid,
_ => false,
}
}
pub fn name(&self) -> &str {
unsafe { str::from_utf8_unchecked(CStr::from_ptr(self.name).to_bytes()) }
}
pub fn as_concrete(&self) -> &Guid {
match &self.data {
TypeDefinitionData::Struct(s) => &s.guid,
}
}
pub fn as_struct(&self) -> Option<&StructDefinition> {
let TypeDefinitionData::Struct(s) = &self.data;
Some(s)
}
pub fn size_in_bits(&self) -> usize {
self.size_in_bits
.try_into()
.expect("cannot convert size in bits to platform size")
}
pub fn size_in_bytes(&self) -> usize {
((self.size_in_bits + 7) / 8)
.try_into()
.expect("cannot covert size in bytes to platform size")
}
pub fn alignment(&self) -> usize {
self.alignment
.try_into()
.expect("cannot convert alignment to platform size")
}
}
impl<'a> fmt::Display for TypeDefinition<'a> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name())
}
}
impl<'a> PartialEq for TypeDefinition<'a> {
fn eq(&self, other: &Self) -> bool {
self.size_in_bits == other.size_in_bits
&& self.alignment == other.alignment
&& self.data == other.data
}
}
impl<'a> Eq for TypeDefinition<'a> {}
unsafe impl<'a> Send for TypeDefinition<'a> {}
unsafe impl<'a> Sync for TypeDefinition<'a> {}
impl<'a> TypeDefinitionData<'a> {
pub fn is_struct(&self) -> bool {
matches!(self, TypeDefinitionData::Struct(_))
}
}
pub trait HasStaticTypeName {
fn type_name() -> &'static CStr;
}
#[cfg(test)]
mod tests {
use std::ffi::CString;
use crate::test_utils::{fake_struct_definition, fake_type_definition, FAKE_TYPE_NAME};
use super::TypeDefinitionData;
#[test]
fn test_type_definition_name() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let field_names = &[];
let field_types = &[];
let field_offsets = &[];
let struct_info = fake_struct_definition(
&type_name,
field_names,
field_types,
field_offsets,
Default::default(),
);
let type_definition =
fake_type_definition(&type_name, 1, 1, TypeDefinitionData::Struct(struct_info));
assert_eq!(type_definition.name(), FAKE_TYPE_NAME);
}
#[test]
fn test_type_definition_size_alignment() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let field_names = &[];
let field_types = &[];
let field_offsets = &[];
let struct_info = fake_struct_definition(
&type_name,
field_names,
field_types,
field_offsets,
Default::default(),
);
let type_definition =
fake_type_definition(&type_name, 24, 8, TypeDefinitionData::Struct(struct_info));
assert_eq!(type_definition.size_in_bits(), 24);
assert_eq!(type_definition.size_in_bytes(), 3);
assert_eq!(type_definition.alignment(), 8);
}
#[test]
fn test_type_definition_group_struct() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let field_names = &[];
let field_types = &[];
let field_offsets = &[];
let struct_info = fake_struct_definition(
&type_name,
field_names,
field_types,
field_offsets,
Default::default(),
);
let type_definition =
fake_type_definition(&type_name, 1, 1, TypeDefinitionData::Struct(struct_info));
assert!(type_definition.data.is_struct());
}
#[test]
fn test_type_definition_eq() {
let type_name = CString::new(FAKE_TYPE_NAME).expect("Invalid fake type name.");
let field_names = &[];
let field_types = &[];
let field_offsets = &[];
let struct_info = fake_struct_definition(
&type_name,
field_names,
field_types,
field_offsets,
Default::default(),
);
let type_definition =
fake_type_definition(&type_name, 1, 1, TypeDefinitionData::Struct(struct_info));
assert_eq!(type_definition, type_definition);
}
}