1#![warn(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4use core::fmt;
9use std::alloc::Layout;
10
11use facet_opaque::OpaqueUninit;
12use typeid::ConstTypeId;
13
14mod list;
15pub use list::*;
16
17mod map;
18pub use map::*;
19
20mod value;
21pub use value::*;
22
23#[derive(Clone, Copy, Debug)]
25pub struct Shape {
26 pub layout: Layout,
28
29 pub vtable: &'static ValueVTable,
33
34 pub def: Def,
36}
37
38impl Shape {
39 pub const fn is(&'static self, characteristic: Characteristic) -> bool {
41 match characteristic {
42 Characteristic::Send => self.vtable.marker_traits.contains(MarkerTraits::SEND),
44 Characteristic::Sync => self.vtable.marker_traits.contains(MarkerTraits::SYNC),
45 Characteristic::Copy => self.vtable.marker_traits.contains(MarkerTraits::COPY),
46 Characteristic::Eq => self.vtable.marker_traits.contains(MarkerTraits::EQ),
47
48 Characteristic::Clone => self.vtable.clone_into.is_some(),
50 Characteristic::Debug => self.vtable.debug.is_some(),
51 Characteristic::PartialEq => self.vtable.eq.is_some(),
52 Characteristic::PartialOrd => self.vtable.partial_ord.is_some(),
53 Characteristic::Ord => self.vtable.ord.is_some(),
54 Characteristic::Hash => self.vtable.hash.is_some(),
55 Characteristic::Default => self.vtable.default_in_place.is_some(),
56 }
57 }
58
59 pub const fn is_send(&'static self) -> bool {
61 self.is(Characteristic::Send)
62 }
63
64 pub const fn is_sync(&'static self) -> bool {
66 self.is(Characteristic::Sync)
67 }
68
69 pub const fn is_copy(&'static self) -> bool {
71 self.is(Characteristic::Copy)
72 }
73
74 pub const fn is_eq(&'static self) -> bool {
76 self.is(Characteristic::Eq)
77 }
78
79 pub const fn is_clone(&'static self) -> bool {
81 self.is(Characteristic::Clone)
82 }
83
84 pub const fn is_debug(&'static self) -> bool {
86 self.is(Characteristic::Debug)
87 }
88
89 pub const fn is_partial_eq(&'static self) -> bool {
91 self.is(Characteristic::PartialEq)
92 }
93
94 pub const fn is_partial_ord(&'static self) -> bool {
96 self.is(Characteristic::PartialOrd)
97 }
98
99 pub const fn is_ord(&'static self) -> bool {
101 self.is(Characteristic::Ord)
102 }
103
104 pub const fn is_hash(&'static self) -> bool {
106 self.is(Characteristic::Hash)
107 }
108
109 pub const fn is_default(&'static self) -> bool {
111 self.is(Characteristic::Default)
112 }
113
114 pub fn write_type_name(&self, f: &mut fmt::Formatter<'_>, opts: TypeNameOpts) -> fmt::Result {
116 (self.vtable.type_name)(f, opts)
117 }
118}
119
120impl PartialEq for Shape {
121 fn eq(&self, other: &Self) -> bool {
122 self.def == other.def && self.layout == other.layout
123 }
124}
125
126impl Eq for Shape {}
127
128impl std::hash::Hash for Shape {
129 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
130 self.def.hash(state);
131 self.layout.hash(state);
132 }
133}
134
135impl Shape {
136 pub fn is_shape(&'static self, other: &'static Shape) -> bool {
138 self == other
139 }
140
141 pub fn assert_shape(&'static self, other: &'static Shape) {
143 assert!(
144 self.is_shape(other),
145 "Shape mismatch: expected {other}, found {self}",
146 );
147 }
148}
149
150impl std::fmt::Display for Shape {
152 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
153 (self.vtable.type_name)(f, TypeNameOpts::default())
154 }
155}
156
157impl Shape {
158 #[inline]
160 pub fn allocate(&self) -> OpaqueUninit<'static> {
161 OpaqueUninit::new(unsafe { std::alloc::alloc(self.layout) })
162 }
163}
164
165#[derive(Debug, Copy, Clone, PartialEq, Eq)]
167pub enum FieldError {
168 NoStaticFields,
172
173 NoSuchStaticField,
176
177 IndexOutOfBounds,
180
181 NotAStruct,
183}
184
185impl std::error::Error for FieldError {}
186
187impl std::fmt::Display for FieldError {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 match self {
190 FieldError::NoStaticFields => write!(f, "No static fields available"),
191 FieldError::NoSuchStaticField => write!(f, "No such static field"),
192 FieldError::IndexOutOfBounds => write!(f, "Index out of bounds"),
193 FieldError::NotAStruct => write!(f, "Not a struct"),
194 }
195 }
196}
197
198#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
200pub struct StructDef {
201 pub kind: StructKind,
203
204 pub fields: &'static [Field],
206}
207
208#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
210pub enum StructKind {
211 Struct,
213
214 TupleStruct,
216
217 Tuple,
219}
220
221#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
223pub struct Field {
224 pub name: &'static str,
226
227 pub shape: &'static Shape,
229
230 pub offset: usize,
232
233 pub flags: FieldFlags,
235}
236
237bitflags::bitflags! {
238 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
240 pub struct FieldFlags: u64 {
241 const EMPTY = 0;
243
244 const SENSITIVE = 1 << 0;
246 }
247}
248
249impl Default for FieldFlags {
250 #[inline(always)]
251 fn default() -> Self {
252 Self::EMPTY
253 }
254}
255
256impl std::fmt::Display for FieldFlags {
257 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
258 if self.is_empty() {
259 return write!(f, "none");
260 }
261
262 let flags = [
264 (FieldFlags::SENSITIVE, "sensitive"),
265 ];
269
270 let mut is_first = true;
272 for (flag, name) in flags {
273 if self.contains(flag) {
274 if !is_first {
275 write!(f, ", ")?;
276 }
277 is_first = false;
278 write!(f, "{}", name)?;
279 }
280 }
281
282 Ok(())
283 }
284}
285
286#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
288pub struct MapDef {
289 pub vtable: &'static MapVTable,
291 pub k: &'static Shape,
293 pub v: &'static Shape,
295}
296
297#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
299pub struct ListDef {
300 pub vtable: &'static ListVTable,
302 pub t: &'static Shape,
304}
305
306#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
308pub struct EnumDef {
309 pub repr: EnumRepr,
311 pub variants: &'static [Variant],
313}
314
315#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
317pub struct Variant {
318 pub name: &'static str,
320
321 pub discriminant: Option<i64>,
323
324 pub kind: VariantKind,
326}
327
328#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
330pub enum VariantKind {
331 Unit,
333
334 Tuple {
336 fields: &'static [Field],
338 },
339
340 Struct {
342 fields: &'static [Field],
344 },
345}
346
347#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
349pub enum EnumRepr {
350 Default,
352 U8,
354 U16,
356 U32,
358 U64,
360 USize,
362 I8,
364 I16,
366 I32,
368 I64,
370 ISize,
372}
373
374impl Default for EnumRepr {
375 fn default() -> Self {
376 Self::Default
377 }
378}
379
380#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
382pub struct ScalarDef {
383 pub type_id: ConstTypeId,
385}
386
387impl ScalarDef {
388 pub const fn of<T>() -> Self {
390 Self {
391 type_id: ConstTypeId::of::<T>(),
392 }
393 }
394}
395
396#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
398pub enum Def {
399 Scalar(ScalarDef),
404
405 Struct(StructDef),
409
410 Map(MapDef),
414
415 List(ListDef),
419
420 Enum(EnumDef),
424}
425
426#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
428pub enum Characteristic {
429 Send,
432
433 Sync,
435
436 Copy,
438
439 Eq,
441
442 Clone,
445
446 Debug,
448
449 PartialEq,
451
452 PartialOrd,
454
455 Ord,
457
458 Hash,
460
461 Default,
463}
464
465impl Characteristic {
466 pub const fn all(self, shapes: &'static [&'static Shape]) -> bool {
468 let mut i = 0;
469 while i < shapes.len() {
470 if !shapes[i].is(self) {
471 return false;
472 }
473 i += 1;
474 }
475 true
476 }
477
478 pub const fn any(self, shapes: &'static [&'static Shape]) -> bool {
480 let mut i = 0;
481 while i < shapes.len() {
482 if shapes[i].is(self) {
483 return true;
484 }
485 i += 1;
486 }
487 false
488 }
489
490 pub const fn none(self, shapes: &'static [&'static Shape]) -> bool {
492 let mut i = 0;
493 while i < shapes.len() {
494 if shapes[i].is(self) {
495 return false;
496 }
497 i += 1;
498 }
499 true
500 }
501}