1#![warn(missing_docs)]
2#![doc = include_str!("../README.md")]
3
4use core::alloc::Layout;
9use core::fmt;
10
11use typeid::ConstTypeId;
12
13mod list;
14pub use list::*;
15
16mod map;
17pub use map::*;
18
19mod value;
20pub use value::*;
21
22#[derive(Clone, Copy, Debug)]
24#[non_exhaustive]
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 pub const fn builder() -> ShapeBuilder {
121 ShapeBuilder::new()
122 }
123}
124
125pub struct ShapeBuilder {
127 layout: Option<Layout>,
128 vtable: Option<&'static ValueVTable>,
129 def: Option<Def>,
130}
131
132impl ShapeBuilder {
133 #[allow(clippy::new_without_default)]
135 pub const fn new() -> Self {
136 Self {
137 layout: None,
138 vtable: None,
139 def: None,
140 }
141 }
142
143 #[inline]
145 pub const fn layout(mut self, layout: Layout) -> Self {
146 self.layout = Some(layout);
147 self
148 }
149
150 #[inline]
152 pub const fn vtable(mut self, vtable: &'static ValueVTable) -> Self {
153 self.vtable = Some(vtable);
154 self
155 }
156
157 #[inline]
159 pub const fn def(mut self, def: Def) -> Self {
160 self.def = Some(def);
161 self
162 }
163
164 #[inline]
170 pub const fn build(self) -> Shape {
171 Shape {
172 layout: self.layout.unwrap(),
173 vtable: self.vtable.unwrap(),
174 def: self.def.unwrap(),
175 }
176 }
177}
178
179impl PartialEq for Shape {
180 fn eq(&self, other: &Self) -> bool {
181 self.def == other.def && self.layout == other.layout
182 }
183}
184
185impl Eq for Shape {}
186
187impl core::hash::Hash for Shape {
188 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
189 self.def.hash(state);
190 self.layout.hash(state);
191 }
192}
193
194impl Shape {
195 pub fn is_shape(&'static self, other: &'static Shape) -> bool {
197 self == other
198 }
199
200 pub fn assert_shape(&'static self, other: &'static Shape) {
202 assert!(
203 self.is_shape(other),
204 "Shape mismatch: expected {other}, found {self}",
205 );
206 }
207}
208
209impl core::fmt::Display for Shape {
211 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
212 (self.vtable.type_name)(f, TypeNameOpts::default())
213 }
214}
215
216impl Shape {
217 #[cfg(feature = "std")]
219 #[inline]
220 pub fn allocate(&self) -> facet_opaque::OpaqueUninit<'static> {
221 facet_opaque::OpaqueUninit::new(unsafe { std::alloc::alloc(self.layout) })
222 }
223}
224
225#[derive(Debug, Copy, Clone, PartialEq, Eq)]
227#[non_exhaustive]
228pub enum FieldError {
229 NoStaticFields,
233
234 NoSuchStaticField,
237
238 IndexOutOfBounds,
241
242 NotAStruct,
244}
245
246impl core::error::Error for FieldError {}
247
248impl core::fmt::Display for FieldError {
249 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
250 match self {
251 FieldError::NoStaticFields => write!(f, "No static fields available"),
252 FieldError::NoSuchStaticField => write!(f, "No such static field"),
253 FieldError::IndexOutOfBounds => write!(f, "Index out of bounds"),
254 FieldError::NotAStruct => write!(f, "Not a struct"),
255 }
256 }
257}
258
259#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
261#[non_exhaustive]
262pub struct StructDef {
263 pub kind: StructKind,
265
266 pub fields: &'static [Field],
268}
269
270impl StructDef {
271 pub const fn builder() -> StructDefBuilder {
273 StructDefBuilder::new()
274 }
275}
276
277pub struct StructDefBuilder {
279 kind: Option<StructKind>,
280 fields: Option<&'static [Field]>,
281}
282
283impl StructDefBuilder {
284 #[allow(clippy::new_without_default)]
286 pub const fn new() -> Self {
287 Self {
288 kind: None,
289 fields: None,
290 }
291 }
292
293 pub const fn kind(mut self, kind: StructKind) -> Self {
295 self.kind = Some(kind);
296 self
297 }
298
299 pub const fn fields(mut self, fields: &'static [Field]) -> Self {
301 self.fields = Some(fields);
302 self
303 }
304
305 pub const fn build(self) -> StructDef {
307 StructDef {
308 kind: self.kind.unwrap(),
309 fields: self.fields.unwrap(),
310 }
311 }
312}
313
314#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
316#[non_exhaustive]
317pub enum StructKind {
318 Struct,
320
321 TupleStruct,
323
324 Tuple,
326}
327
328#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
330#[non_exhaustive]
331pub struct Field {
332 pub name: &'static str,
334
335 pub shape: &'static Shape,
337
338 pub offset: usize,
340
341 pub flags: FieldFlags,
343}
344
345impl Field {
346 pub const fn builder() -> FieldBuilder {
348 FieldBuilder::new()
349 }
350}
351
352pub struct FieldBuilder {
354 name: Option<&'static str>,
355 shape: Option<&'static Shape>,
356 offset: Option<usize>,
357 flags: Option<FieldFlags>,
358}
359
360impl FieldBuilder {
361 #[allow(clippy::new_without_default)]
363 pub const fn new() -> Self {
364 Self {
365 name: None,
366 shape: None,
367 offset: None,
368 flags: None,
369 }
370 }
371
372 pub const fn name(mut self, name: &'static str) -> Self {
374 self.name = Some(name);
375 self
376 }
377
378 pub const fn shape(mut self, shape: &'static Shape) -> Self {
380 self.shape = Some(shape);
381 self
382 }
383
384 pub const fn offset(mut self, offset: usize) -> Self {
386 self.offset = Some(offset);
387 self
388 }
389
390 pub const fn flags(mut self, flags: FieldFlags) -> Self {
392 self.flags = Some(flags);
393 self
394 }
395
396 pub const fn build(self) -> Field {
398 Field {
399 name: self.name.unwrap(),
400 shape: self.shape.unwrap(),
401 offset: self.offset.unwrap(),
402 flags: match self.flags {
403 Some(flags) => flags,
404 None => FieldFlags::EMPTY,
405 },
406 }
407 }
408}
409
410bitflags::bitflags! {
411 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
413 pub struct FieldFlags: u64 {
414 const EMPTY = 0;
416
417 const SENSITIVE = 1 << 0;
419 }
420}
421
422impl Default for FieldFlags {
423 #[inline(always)]
424 fn default() -> Self {
425 Self::EMPTY
426 }
427}
428
429impl core::fmt::Display for FieldFlags {
430 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
431 if self.is_empty() {
432 return write!(f, "none");
433 }
434
435 let flags = [
437 (FieldFlags::SENSITIVE, "sensitive"),
438 ];
442
443 let mut is_first = true;
445 for (flag, name) in flags {
446 if self.contains(flag) {
447 if !is_first {
448 write!(f, ", ")?;
449 }
450 is_first = false;
451 write!(f, "{}", name)?;
452 }
453 }
454
455 Ok(())
456 }
457}
458
459#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
461#[non_exhaustive]
462pub struct MapDef {
463 pub vtable: &'static MapVTable,
465 pub k: &'static Shape,
467 pub v: &'static Shape,
469}
470
471impl MapDef {
472 pub const fn builder() -> MapDefBuilder {
474 MapDefBuilder::new()
475 }
476}
477
478pub struct MapDefBuilder {
480 vtable: Option<&'static MapVTable>,
481 k: Option<&'static Shape>,
482 v: Option<&'static Shape>,
483}
484
485impl MapDefBuilder {
486 #[allow(clippy::new_without_default)]
488 pub const fn new() -> Self {
489 Self {
490 vtable: None,
491 k: None,
492 v: None,
493 }
494 }
495
496 pub const fn vtable(mut self, vtable: &'static MapVTable) -> Self {
498 self.vtable = Some(vtable);
499 self
500 }
501
502 pub const fn k(mut self, k: &'static Shape) -> Self {
504 self.k = Some(k);
505 self
506 }
507
508 pub const fn v(mut self, v: &'static Shape) -> Self {
510 self.v = Some(v);
511 self
512 }
513
514 pub const fn build(self) -> MapDef {
516 MapDef {
517 vtable: self.vtable.unwrap(),
518 k: self.k.unwrap(),
519 v: self.v.unwrap(),
520 }
521 }
522}
523
524#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
526#[non_exhaustive]
527pub struct ListDef {
528 pub vtable: &'static ListVTable,
530 pub t: &'static Shape,
532}
533
534impl ListDef {
535 pub const fn builder() -> ListDefBuilder {
537 ListDefBuilder::new()
538 }
539}
540
541pub struct ListDefBuilder {
543 vtable: Option<&'static ListVTable>,
544 t: Option<&'static Shape>,
545}
546
547impl ListDefBuilder {
548 #[allow(clippy::new_without_default)]
550 pub const fn new() -> Self {
551 Self {
552 vtable: None,
553 t: None,
554 }
555 }
556
557 pub const fn vtable(mut self, vtable: &'static ListVTable) -> Self {
559 self.vtable = Some(vtable);
560 self
561 }
562
563 pub const fn t(mut self, t: &'static Shape) -> Self {
565 self.t = Some(t);
566 self
567 }
568
569 pub const fn build(self) -> ListDef {
571 ListDef {
572 vtable: self.vtable.unwrap(),
573 t: self.t.unwrap(),
574 }
575 }
576}
577
578#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
580#[non_exhaustive]
581pub struct EnumDef {
582 pub repr: EnumRepr,
584 pub variants: &'static [Variant],
586}
587
588impl EnumDef {
589 pub const fn builder() -> EnumDefBuilder {
591 EnumDefBuilder::new()
592 }
593}
594
595pub struct EnumDefBuilder {
597 repr: Option<EnumRepr>,
598 variants: Option<&'static [Variant]>,
599}
600
601impl EnumDefBuilder {
602 #[allow(clippy::new_without_default)]
604 pub const fn new() -> Self {
605 Self {
606 repr: None,
607 variants: None,
608 }
609 }
610
611 pub const fn repr(mut self, repr: EnumRepr) -> Self {
613 self.repr = Some(repr);
614 self
615 }
616
617 pub const fn variants(mut self, variants: &'static [Variant]) -> Self {
619 self.variants = Some(variants);
620 self
621 }
622
623 pub const fn build(self) -> EnumDef {
625 EnumDef {
626 repr: self.repr.unwrap(),
627 variants: self.variants.unwrap(),
628 }
629 }
630}
631
632#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
634#[non_exhaustive]
635pub struct Variant {
636 pub name: &'static str,
638
639 pub discriminant: Option<i64>,
641
642 pub kind: VariantKind,
644}
645
646impl Variant {
647 pub const fn builder() -> VariantBuilder {
649 VariantBuilder::new()
650 }
651}
652
653pub struct VariantBuilder {
655 name: Option<&'static str>,
656 discriminant: Option<Option<i64>>,
657 kind: Option<VariantKind>,
658}
659
660impl VariantBuilder {
661 #[allow(clippy::new_without_default)]
663 pub const fn new() -> Self {
664 Self {
665 name: None,
666 discriminant: None,
667 kind: None,
668 }
669 }
670
671 pub const fn name(mut self, name: &'static str) -> Self {
673 self.name = Some(name);
674 self
675 }
676
677 pub const fn discriminant(mut self, discriminant: Option<i64>) -> Self {
679 self.discriminant = Some(discriminant);
680 self
681 }
682
683 pub const fn kind(mut self, kind: VariantKind) -> Self {
685 self.kind = Some(kind);
686 self
687 }
688
689 pub const fn build(self) -> Variant {
691 Variant {
692 name: self.name.unwrap(),
693 discriminant: self.discriminant.unwrap(),
694 kind: self.kind.unwrap(),
695 }
696 }
697}
698
699#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
701#[non_exhaustive]
702pub enum VariantKind {
703 Unit,
705
706 Tuple {
708 fields: &'static [Field],
710 },
711
712 Struct {
714 fields: &'static [Field],
716 },
717}
718
719#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
721#[non_exhaustive]
722pub enum EnumRepr {
723 Default,
725 U8,
727 U16,
729 U32,
731 U64,
733 USize,
735 I8,
737 I16,
739 I32,
741 I64,
743 ISize,
745}
746
747impl Default for EnumRepr {
748 fn default() -> Self {
749 Self::Default
750 }
751}
752
753#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
755#[non_exhaustive]
756pub struct ScalarDef {
757 pub type_id: ConstTypeId,
759}
760
761impl ScalarDef {
762 pub const fn of<T>() -> Self {
764 ScalarDefBuilder::new()
765 .type_id(ConstTypeId::of::<T>())
766 .build()
767 }
768}
769
770#[derive(Default)]
772pub struct ScalarDefBuilder {
773 type_id: Option<ConstTypeId>,
774}
775
776impl ScalarDefBuilder {
777 #[allow(clippy::new_without_default)]
779 pub const fn new() -> Self {
780 Self { type_id: None }
781 }
782
783 pub const fn type_id(mut self, type_id: ConstTypeId) -> Self {
785 self.type_id = Some(type_id);
786 self
787 }
788
789 pub const fn build(self) -> ScalarDef {
791 ScalarDef {
792 type_id: self.type_id.unwrap(),
793 }
794 }
795}
796
797#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
799#[non_exhaustive]
800pub enum Def {
801 Scalar(ScalarDef),
806
807 Struct(StructDef),
811
812 Map(MapDef),
816
817 List(ListDef),
821
822 Enum(EnumDef),
826}
827
828#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
830#[non_exhaustive]
831pub enum Characteristic {
832 Send,
835
836 Sync,
838
839 Copy,
841
842 Eq,
844
845 Clone,
848
849 Debug,
851
852 PartialEq,
854
855 PartialOrd,
857
858 Ord,
860
861 Hash,
863
864 Default,
866}
867
868impl Characteristic {
869 pub const fn all(self, shapes: &'static [&'static Shape]) -> bool {
871 let mut i = 0;
872 while i < shapes.len() {
873 if !shapes[i].is(self) {
874 return false;
875 }
876 i += 1;
877 }
878 true
879 }
880
881 pub const fn any(self, shapes: &'static [&'static Shape]) -> bool {
883 let mut i = 0;
884 while i < shapes.len() {
885 if shapes[i].is(self) {
886 return true;
887 }
888 i += 1;
889 }
890 false
891 }
892
893 pub const fn none(self, shapes: &'static [&'static Shape]) -> bool {
895 let mut i = 0;
896 while i < shapes.len() {
897 if shapes[i].is(self) {
898 return false;
899 }
900 i += 1;
901 }
902 true
903 }
904}