1use std::{alloc::Layout, any::TypeId, collections::HashSet, fmt::Formatter, ptr::NonNull};
2
3#[derive(Clone, Copy)]
5pub struct Shape {
6 pub name: NameFn,
8
9 pub typeid: TypeId,
11
12 pub layout: Layout,
14
15 pub innards: Innards,
17
18 pub set_to_default: Option<SetToDefaultFn>,
20
21 pub drop_in_place: Option<DropFn>,
28}
29
30pub type NameFn = fn(f: &mut std::fmt::Formatter) -> std::fmt::Result;
31
32impl std::fmt::Display for Shape {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 (self.name)(f)
36 }
37}
38
39pub type SetToDefaultFn = unsafe fn(*mut u8);
41
42pub type DropFn = unsafe fn(*mut u8);
44
45impl PartialEq for Shape {
46 fn eq(&self, other: &Self) -> bool {
47 self.typeid == other.typeid
48 }
49}
50
51impl Eq for Shape {}
52
53impl std::hash::Hash for Shape {
54 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
55 self.typeid.hash(state);
56 }
57}
58
59impl Shape {
60 const INDENT: usize = 2;
61
62 pub unsafe fn get_scalar_contents<'a>(&self, ptr: *const u8) -> crate::ScalarContents<'a> {
72 match self.innards {
73 Innards::Scalar(scalar) => unsafe { scalar.get_contents(ptr) },
74 _ => panic!("Expected a scalar shape"),
75 }
76 }
77
78 pub fn pretty_print_recursive(&self, f: &mut Formatter) -> std::fmt::Result {
80 self.pretty_print_recursive_internal(f, &mut HashSet::new(), 0)
81 }
82
83 fn pretty_print_recursive_internal(
84 &self,
85 f: &mut Formatter,
86 printed_schemas: &mut HashSet<Shape>,
87 indent: usize,
88 ) -> std::fmt::Result {
89 if !printed_schemas.insert(*self) {
90 write!(f, "{:indent$}\x1b[1;33m", "", indent = indent)?;
91 (self.name)(f)?;
92 writeln!(f, "\x1b[0m (\x1b[1;31malready printed\x1b[0m)")?;
93 return Ok(());
94 }
95
96 write!(f, "{:indent$}\x1b[1;33m", "", indent = indent)?;
97 (self.name)(f)?;
98 writeln!(
99 f,
100 "\x1b[0m (size: \x1b[1;34m{}\x1b[0m, align: \x1b[1;35m{}\x1b[0m)",
101 self.layout.size(),
102 self.layout.align()
103 )?;
104
105 match &self.innards {
106 Innards::Struct { fields } => {
107 for field in *fields {
108 writeln!(
109 f,
110 "{:indent$}\x1b[1;32m{}\x1b[0m: ",
111 "",
112 field.name,
113 indent = indent + Self::INDENT
114 )?;
115 if field.flags.is_sensitive() {
116 writeln!(
117 f,
118 "{:indent$}[REDACTED]",
119 "",
120 indent = indent + Self::INDENT * 2
121 )?;
122 } else {
123 field.shape.get().pretty_print_recursive_internal(
124 f,
125 printed_schemas,
126 indent + Self::INDENT * 2,
127 )?;
128 }
129 }
130 }
131 Innards::HashMap { value_shape } => {
132 writeln!(
133 f,
134 "{:indent$}\x1b[1;36mHashMap with arbitrary keys and value shape:\x1b[0m",
135 "",
136 indent = indent + Self::INDENT
137 )?;
138 value_shape.get().pretty_print_recursive_internal(
139 f,
140 printed_schemas,
141 indent + Self::INDENT * 2,
142 )?;
143 }
144 Innards::Array(elem_schema) => {
145 write!(
146 f,
147 "{:indent$}\x1b[1;36mArray of:\x1b[0m ",
148 "",
149 indent = indent + Self::INDENT
150 )?;
151 elem_schema.get().pretty_print_recursive_internal(
152 f,
153 printed_schemas,
154 indent + Self::INDENT * 2,
155 )?;
156 }
157 Innards::Transparent(inner_schema) => {
158 write!(
159 f,
160 "{:indent$}\x1b[1;36mTransparent wrapper for:\x1b[0m ",
161 "",
162 indent = indent + Self::INDENT
163 )?;
164 inner_schema.get().pretty_print_recursive_internal(
165 f,
166 printed_schemas,
167 indent + Self::INDENT * 2,
168 )?;
169 }
170 Innards::Scalar(scalar) => {
171 writeln!(
172 f,
173 "{:indent$}\x1b[1;36mScalar:\x1b[0m \x1b[1;33m{:?}\x1b[0m",
174 "",
175 scalar,
176 indent = indent
177 )?;
178 }
179 }
180
181 Ok(())
182 }
183
184 pub fn known_fields(&self) -> &'static [Field] {
186 match self.innards {
187 Innards::Struct { fields } => fields,
188 _ => &[],
189 }
190 }
191
192 pub fn field_by_name(&self, name: &str) -> Option<&Field> {
194 self.known_fields().iter().find(|field| field.name == name)
195 }
196
197 pub fn field_by_index(&self, index: usize) -> Result<&Field, FieldError> {
199 match self.innards {
200 Innards::Struct { fields } => fields.get(index).ok_or(FieldError::IndexOutOfBounds),
201 _ => Err(FieldError::NotAStruct),
202 }
203 }
204
205 pub fn dangling(&self) -> NonNull<u8> {
215 let dang = NonNull::dangling();
216 let offset = dang.align_offset(self.layout.align());
217 unsafe { dang.byte_add(offset) }
218 }
219}
220
221#[derive(Debug)]
223pub enum FieldError {
224 NoStaticFields,
228
229 NoSuchStaticField,
232
233 IndexOutOfBounds,
236
237 NotAStruct,
239}
240
241impl std::error::Error for FieldError {}
242
243impl std::fmt::Display for FieldError {
244 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
245 match self {
246 FieldError::NoStaticFields => write!(f, "No static fields available"),
247 FieldError::NoSuchStaticField => write!(f, "No such static field"),
248 FieldError::IndexOutOfBounds => write!(f, "Index out of bounds"),
249 FieldError::NotAStruct => write!(f, "Not a struct"),
250 }
251 }
252}
253
254impl std::fmt::Debug for Shape {
255 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
256 self.pretty_print_recursive(f)
257 }
258}
259
260#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
262pub enum Innards {
263 Struct { fields: &'static [Field] },
265
266 HashMap { value_shape: ShapeDesc },
268
269 Array(ShapeDesc),
271
272 Transparent(ShapeDesc),
274
275 Scalar(Scalar),
277}
278
279#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
280pub struct Field {
281 pub name: &'static str,
283
284 pub shape: ShapeDesc,
286
287 pub offset: usize,
297
298 pub flags: FieldFlags,
300}
301
302#[derive(Debug, Clone, Copy)]
304pub enum SetFieldOutcome {
305 Accepted,
307
308 Rejected,
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
313#[non_exhaustive]
314pub enum Scalar {
315 String,
317
318 Bytes,
320
321 I8,
322 I16,
323 I32,
324 I64,
325 I128,
326
327 U8,
328 U16,
329 U32,
330 U64,
331 U128,
332
333 F32,
334 F64,
335
336 Boolean,
337
338 Nothing,
340}
341
342#[derive(Clone, Copy)]
344pub struct ShapeDesc(pub fn() -> Shape);
345
346impl From<fn() -> Shape> for ShapeDesc {
347 fn from(f: fn() -> Shape) -> Self {
348 Self(f)
349 }
350}
351
352impl ShapeDesc {
353 #[inline(always)]
355 pub fn get(&self) -> Shape {
356 (self.0)()
357 }
358}
359
360impl PartialEq for ShapeDesc {
361 fn eq(&self, other: &Self) -> bool {
362 if std::ptr::eq(self.0 as *const (), other.0 as *const ()) {
363 true
364 } else {
365 let self_shape = self.0();
366 let other_shape = other.0();
367 self_shape == other_shape
368 }
369 }
370}
371
372impl Eq for ShapeDesc {}
373
374impl std::hash::Hash for ShapeDesc {
375 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
376 (self.0 as *const ()).hash(state);
378 }
379}
380
381impl std::fmt::Debug for ShapeDesc {
382 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
383 self.get().fmt(f)
384 }
385}
386
387#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
402pub struct FieldFlags(u64);
403
404impl FieldFlags {
405 pub const EMPTY: Self = Self(0);
407
408 pub const SENSITIVE: Self = Self(1 << 0);
410
411 #[inline]
413 pub fn is_sensitive(&self) -> bool {
414 self.0 & Self::SENSITIVE.0 != 0
415 }
416
417 #[inline]
419 pub fn set_sensitive(&mut self) -> &mut Self {
420 self.0 |= Self::SENSITIVE.0;
421 self
422 }
423
424 #[inline]
426 pub fn unset_sensitive(&mut self) -> &mut Self {
427 self.0 &= !Self::SENSITIVE.0;
428 self
429 }
430
431 #[inline]
433 pub const fn sensitive() -> Self {
434 Self::SENSITIVE
435 }
436}
437
438impl std::ops::BitOr for FieldFlags {
439 type Output = Self;
440
441 fn bitor(self, rhs: Self) -> Self {
442 Self(self.0 | rhs.0)
443 }
444}
445
446impl std::ops::BitOrAssign for FieldFlags {
447 fn bitor_assign(&mut self, rhs: Self) {
448 self.0 |= rhs.0;
449 }
450}
451
452impl Default for FieldFlags {
453 #[inline(always)]
454 fn default() -> Self {
455 Self::EMPTY
456 }
457}
458
459impl std::fmt::Display for FieldFlags {
460 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
461 if self.0 == 0 {
462 return write!(f, "none");
463 }
464
465 let flags = [
467 (Self::SENSITIVE.0, "sensitive"),
468 ];
472
473 let mut is_first = true;
475 for (bit, name) in flags {
476 if self.0 & bit != 0 {
477 if !is_first {
478 write!(f, ", ")?;
479 }
480 is_first = false;
481 write!(f, "{}", name)?;
482 }
483 }
484
485 Ok(())
486 }
487}