1use std::{ffi::CStr, os::raw::c_char, slice, str};
2
3use crate::type_id::TypeId;
4use crate::Guid;
5
6#[repr(C)]
8#[derive(Debug)]
9pub struct StructDefinition<'a> {
10 pub guid: Guid,
12 pub field_names: *const *const c_char,
14 pub(crate) field_types: *const TypeId<'a>,
16 pub(crate) field_offsets: *const u16,
18 pub(crate) num_fields: u16,
22 pub memory_kind: StructMemoryKind,
25}
26
27#[repr(u8)]
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30#[cfg_attr(feature = "serde", derive(serde::Serialize))]
31pub enum StructMemoryKind {
32 Gc,
35
36 Value,
41}
42
43impl<'a> StructDefinition<'a> {
44 pub fn field_names(&self) -> impl Iterator<Item = &str> {
46 let field_names = if self.num_fields == 0 {
47 &[]
48 } else {
49 unsafe { slice::from_raw_parts(self.field_names, self.num_fields as usize) }
50 };
51
52 field_names
53 .iter()
54 .map(|n| unsafe { str::from_utf8_unchecked(CStr::from_ptr(*n).to_bytes()) })
55 }
56
57 pub fn field_types(&self) -> &[TypeId<'a>] {
59 if self.num_fields == 0 {
60 &[]
61 } else {
62 unsafe { slice::from_raw_parts(self.field_types, self.num_fields as usize) }
63 }
64 }
65
66 pub fn field_offsets(&self) -> &[u16] {
68 if self.num_fields == 0 {
69 &[]
70 } else {
71 unsafe { slice::from_raw_parts(self.field_offsets, self.num_fields as usize) }
72 }
73 }
74
75 pub fn num_fields(&self) -> usize {
77 self.num_fields.into()
78 }
79}
80
81impl Default for StructMemoryKind {
82 fn default() -> Self {
83 StructMemoryKind::Gc
84 }
85}
86
87impl<'a> PartialEq for StructDefinition<'a> {
88 fn eq(&self, other: &Self) -> bool {
89 self.guid == other.guid
90 }
91}
92
93impl<'a> Eq for StructDefinition<'a> {}
94
95#[cfg(feature = "serde")]
96impl<'a> serde::Serialize for StructDefinition<'a> {
97 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
98 where
99 S: serde::Serializer,
100 {
101 use itertools::Itertools;
102 use serde::ser::SerializeStruct;
103
104 let mut s = serializer.serialize_struct("StructInfo", 3)?;
105
106 #[derive(serde::Serialize)]
107 struct Field<'a> {
108 name: &'a str,
109 r#type: &'a TypeId<'a>,
110 offset: &'a u16,
111 }
112
113 s.serialize_field("guid", &self.guid)?;
114 s.serialize_field(
115 "fields",
116 &self
117 .field_names()
118 .zip(self.field_types())
119 .zip(self.field_offsets())
120 .map(|((name, ty), offset)| Field {
121 name,
122 r#type: ty,
123 offset,
124 })
125 .collect_vec(),
126 )?;
127 s.serialize_field("memory_kind", &self.memory_kind)?;
128 s.end()
129 }
130}
131
132#[cfg(test)]
133mod tests {
134 use crate::type_id::HasStaticTypeId;
135 use std::ffi::CString;
136
137 use crate::test_utils::{fake_struct_definition, FAKE_FIELD_NAME, FAKE_STRUCT_NAME};
138
139 use super::StructMemoryKind;
140
141 #[test]
142 fn test_struct_info_fields_none() {
143 let field_names = &[];
144 let field_types = &[];
145 let field_offsets = &[];
146 let struct_info = fake_struct_definition(
147 &CString::new(FAKE_STRUCT_NAME).unwrap(),
148 field_names,
149 field_types,
150 field_offsets,
151 Default::default(),
152 );
153
154 assert_eq!(struct_info.field_names().count(), 0);
155 assert_eq!(struct_info.field_types(), field_types);
156 assert_eq!(struct_info.field_offsets(), field_offsets);
157 }
158
159 #[test]
160 fn test_struct_info_fields_some() {
161 let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name.");
162 let field_name = CString::new(FAKE_FIELD_NAME).expect("Invalid fake field name.");
163 let type_id = i32::type_id();
164
165 let field_names = &[field_name.as_ptr()];
166 let field_types = &[type_id.clone()];
167 let field_offsets = &[1];
168 let struct_info = fake_struct_definition(
169 &struct_name,
170 field_names,
171 field_types,
172 field_offsets,
173 Default::default(),
174 );
175
176 assert_eq!(struct_info.num_fields(), 1);
177 for (lhs, rhs) in struct_info.field_names().zip([FAKE_FIELD_NAME].iter()) {
178 assert_eq!(lhs, *rhs)
179 }
180 assert_eq!(struct_info.field_types(), field_types);
181 assert_eq!(struct_info.field_offsets(), field_offsets);
182 }
183
184 #[test]
185 fn test_struct_info_memory_kind_gc() {
186 let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name.");
187 let struct_memory_kind = StructMemoryKind::Gc;
188 let struct_info = fake_struct_definition(&struct_name, &[], &[], &[], struct_memory_kind);
189
190 assert_eq!(struct_info.memory_kind, struct_memory_kind);
191 }
192
193 #[test]
194 fn test_struct_info_memory_kind_value() {
195 let struct_name = CString::new(FAKE_STRUCT_NAME).expect("Invalid fake struct name.");
196 let struct_memory_kind = StructMemoryKind::Value;
197 let struct_info = fake_struct_definition(&struct_name, &[], &[], &[], struct_memory_kind);
198
199 assert_eq!(struct_info.memory_kind, struct_memory_kind);
200 }
201}