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 fields: &'static [Field],
203}
204
205#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
207pub struct Field {
208 pub name: &'static str,
210
211 pub shape: &'static Shape,
213
214 pub offset: usize,
216
217 pub flags: FieldFlags,
219}
220
221bitflags::bitflags! {
222 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
224 pub struct FieldFlags: u64 {
225 const EMPTY = 0;
227
228 const SENSITIVE = 1 << 0;
230 }
231}
232
233impl Default for FieldFlags {
234 #[inline(always)]
235 fn default() -> Self {
236 Self::EMPTY
237 }
238}
239
240impl std::fmt::Display for FieldFlags {
241 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242 if self.is_empty() {
243 return write!(f, "none");
244 }
245
246 let flags = [
248 (FieldFlags::SENSITIVE, "sensitive"),
249 ];
253
254 let mut is_first = true;
256 for (flag, name) in flags {
257 if self.contains(flag) {
258 if !is_first {
259 write!(f, ", ")?;
260 }
261 is_first = false;
262 write!(f, "{}", name)?;
263 }
264 }
265
266 Ok(())
267 }
268}
269
270#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
272pub struct MapDef {
273 pub vtable: &'static MapVTable,
275 pub k: &'static Shape,
277 pub v: &'static Shape,
279}
280
281#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
283pub struct ListDef {
284 pub vtable: &'static ListVTable,
286 pub t: &'static Shape,
288}
289
290#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
292pub struct EnumDef {
293 pub repr: EnumRepr,
295 pub variants: &'static [Variant],
297}
298
299#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
301pub struct Variant {
302 pub name: &'static str,
304
305 pub discriminant: Option<i64>,
307
308 pub kind: VariantKind,
310}
311
312#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
314pub enum VariantKind {
315 Unit,
317
318 Tuple {
320 fields: &'static [Field],
322 },
323
324 Struct {
326 fields: &'static [Field],
328 },
329}
330
331#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
333pub enum EnumRepr {
334 Default,
336 U8,
338 U16,
340 U32,
342 U64,
344 USize,
346 I8,
348 I16,
350 I32,
352 I64,
354 ISize,
356}
357
358impl Default for EnumRepr {
359 fn default() -> Self {
360 Self::Default
361 }
362}
363
364#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
366pub struct ScalarDef {
367 pub type_id: ConstTypeId,
369}
370
371impl ScalarDef {
372 pub const fn of<T>() -> Self {
374 Self {
375 type_id: ConstTypeId::of::<T>(),
376 }
377 }
378}
379
380#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
382pub enum Def {
383 Scalar(ScalarDef),
388
389 Struct(StructDef),
393
394 TupleStruct(StructDef),
398
399 Tuple(StructDef),
403
404 Map(MapDef),
408
409 List(ListDef),
413
414 Enum(EnumDef),
418}
419
420#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
422pub enum Characteristic {
423 Send,
426
427 Sync,
429
430 Copy,
432
433 Eq,
435
436 Clone,
439
440 Debug,
442
443 PartialEq,
445
446 PartialOrd,
448
449 Ord,
451
452 Hash,
454
455 Default,
457}
458
459impl Characteristic {
460 pub const fn all(self, shapes: &'static [&'static Shape]) -> bool {
462 let mut i = 0;
463 while i < shapes.len() {
464 if !shapes[i].is(self) {
465 return false;
466 }
467 i += 1;
468 }
469 true
470 }
471
472 pub const fn any(self, shapes: &'static [&'static Shape]) -> bool {
474 let mut i = 0;
475 while i < shapes.len() {
476 if shapes[i].is(self) {
477 return true;
478 }
479 i += 1;
480 }
481 false
482 }
483
484 pub const fn none(self, shapes: &'static [&'static Shape]) -> bool {
486 let mut i = 0;
487 while i < shapes.len() {
488 if shapes[i].is(self) {
489 return false;
490 }
491 i += 1;
492 }
493 true
494 }
495}