use std::{ffi::CStr, os::raw::c_char, slice, str};
use crate::type_id::TypeId;
use crate::Guid;
#[repr(C)]
#[derive(Debug)]
pub struct StructDefinition<'a> {
pub guid: Guid,
pub field_names: *const *const c_char,
pub(crate) field_types: *const TypeId<'a>,
pub(crate) field_offsets: *const u16,
pub(crate) num_fields: u16,
pub memory_kind: StructMemoryKind,
}
#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum StructMemoryKind {
Gc,
Value,
}
impl<'a> StructDefinition<'a> {
pub fn field_names(&self) -> impl Iterator<Item = &str> {
let field_names = if self.num_fields == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(self.field_names, self.num_fields as usize) }
};
field_names
.iter()
.map(|n| unsafe { str::from_utf8_unchecked(CStr::from_ptr(*n).to_bytes()) })
}
pub fn field_types(&self) -> &[TypeId<'a>] {
if self.num_fields == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(self.field_types, self.num_fields as usize) }
}
}
pub fn field_offsets(&self) -> &[u16] {
if self.num_fields == 0 {
&[]
} else {
unsafe { slice::from_raw_parts(self.field_offsets, self.num_fields as usize) }
}
}
pub fn num_fields(&self) -> usize {
self.num_fields.into()
}
}
impl Default for StructMemoryKind {
fn default() -> Self {
StructMemoryKind::Gc
}
}
impl<'a> PartialEq for StructDefinition<'a> {
fn eq(&self, other: &Self) -> bool {
self.guid == other.guid
}
}
impl<'a> Eq for StructDefinition<'a> {}
#[cfg(feature = "serde")]
impl<'a> serde::Serialize for StructDefinition<'a> {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
use itertools::Itertools;
use serde::ser::SerializeStruct;
let mut s = serializer.serialize_struct("StructInfo", 3)?;
#[derive(serde::Serialize)]
struct Field<'a> {
name: &'a str,
r#type: &'a TypeId<'a>,
offset: &'a u16,
}
s.serialize_field("guid", &self.guid)?;
s.serialize_field(
"fields",
&self
.field_names()
.zip(self.field_types())
.zip(self.field_offsets())
.map(|((name, ty), offset)| Field {
name,
r#type: ty,
offset,
})
.collect_vec(),
)?;
s.serialize_field("memory_kind", &self.memory_kind)?;
s.end()
}
}
#[cfg(test)]
mod tests {
use crate::type_id::HasStaticTypeId;
use std::ffi::CString;
use crate::test_utils::{fake_struct_definition, FAKE_FIELD_NAME, FAKE_STRUCT_NAME};
use super::StructMemoryKind;
#[test]
fn test_struct_info_fields_none() {
let field_names = &[];
let field_types = &[];
let field_offsets = &[];
let struct_info = fake_struct_definition(
&CString::new(FAKE_STRUCT_NAME).unwrap(),
field_names,
field_types,
field_offsets,
Default::default(),
);
assert_eq!(struct_info.field_names().count(), 0);
assert_eq!(struct_info.field_types(), field_types);
assert_eq!(struct_info.field_offsets(), field_offsets);
}
#[test]
fn test_struct_info_fields_some() {
let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name.");
let field_name = CString::new(FAKE_FIELD_NAME).expect("Invalid fake field name.");
let type_id = i32::type_id();
let field_names = &[field_name.as_ptr()];
let field_types = &[type_id.clone()];
let field_offsets = &[1];
let struct_info = fake_struct_definition(
&struct_name,
field_names,
field_types,
field_offsets,
Default::default(),
);
assert_eq!(struct_info.num_fields(), 1);
for (lhs, rhs) in struct_info.field_names().zip([FAKE_FIELD_NAME].iter()) {
assert_eq!(lhs, *rhs)
}
assert_eq!(struct_info.field_types(), field_types);
assert_eq!(struct_info.field_offsets(), field_offsets);
}
#[test]
fn test_struct_info_memory_kind_gc() {
let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name.");
let struct_memory_kind = StructMemoryKind::Gc;
let struct_info = fake_struct_definition(&struct_name, &[], &[], &[], struct_memory_kind);
assert_eq!(struct_info.memory_kind, struct_memory_kind);
}
#[test]
fn test_struct_info_memory_kind_value() {
let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name.");
let struct_memory_kind = StructMemoryKind::Value;
let struct_info = fake_struct_definition(&struct_name, &[], &[], &[], struct_memory_kind);
assert_eq!(struct_info.memory_kind, struct_memory_kind);
}
}