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 pub attributes: &'static [FieldAttribute],
346}
347
348impl Field {
349 pub const fn builder() -> FieldBuilder {
351 FieldBuilder::new()
352 }
353}
354
355pub struct FieldBuilder {
357 name: Option<&'static str>,
358 shape: Option<&'static Shape>,
359 offset: Option<usize>,
360 flags: Option<FieldFlags>,
361 attributes: &'static [FieldAttribute],
362}
363
364#[non_exhaustive]
366#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
367pub enum FieldAttribute {
368 Sensitive,
370 Arbitrary(&'static str),
372}
373
374impl FieldBuilder {
375 #[allow(clippy::new_without_default)]
377 pub const fn new() -> Self {
378 Self {
379 name: None,
380 shape: None,
381 offset: None,
382 flags: None,
383 attributes: &[],
384 }
385 }
386
387 pub const fn name(mut self, name: &'static str) -> Self {
389 self.name = Some(name);
390 self
391 }
392
393 pub const fn shape(mut self, shape: &'static Shape) -> Self {
395 self.shape = Some(shape);
396 self
397 }
398
399 pub const fn offset(mut self, offset: usize) -> Self {
401 self.offset = Some(offset);
402 self
403 }
404
405 pub const fn flags(mut self, flags: FieldFlags) -> Self {
407 self.flags = Some(flags);
408 self
409 }
410
411 pub const fn attributes(mut self, attributes: &'static [FieldAttribute]) -> Self {
413 self.attributes = attributes;
414 self
415 }
416
417 pub const fn build(self) -> Field {
419 Field {
420 name: self.name.unwrap(),
421 shape: self.shape.unwrap(),
422 offset: self.offset.unwrap(),
423 flags: match self.flags {
424 Some(flags) => flags,
425 None => FieldFlags::EMPTY,
426 },
427 attributes: self.attributes,
428 }
429 }
430}
431
432bitflags::bitflags! {
433 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
435 pub struct FieldFlags: u64 {
436 const EMPTY = 0;
438
439 const SENSITIVE = 1 << 0;
441 }
442}
443
444impl Default for FieldFlags {
445 #[inline(always)]
446 fn default() -> Self {
447 Self::EMPTY
448 }
449}
450
451impl core::fmt::Display for FieldFlags {
452 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
453 if self.is_empty() {
454 return write!(f, "none");
455 }
456
457 let flags = [
459 (FieldFlags::SENSITIVE, "sensitive"),
460 ];
464
465 let mut is_first = true;
467 for (flag, name) in flags {
468 if self.contains(flag) {
469 if !is_first {
470 write!(f, ", ")?;
471 }
472 is_first = false;
473 write!(f, "{}", name)?;
474 }
475 }
476
477 Ok(())
478 }
479}
480
481#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
483#[non_exhaustive]
484pub struct MapDef {
485 pub vtable: &'static MapVTable,
487 pub k: &'static Shape,
489 pub v: &'static Shape,
491}
492
493impl MapDef {
494 pub const fn builder() -> MapDefBuilder {
496 MapDefBuilder::new()
497 }
498}
499
500pub struct MapDefBuilder {
502 vtable: Option<&'static MapVTable>,
503 k: Option<&'static Shape>,
504 v: Option<&'static Shape>,
505}
506
507impl MapDefBuilder {
508 #[allow(clippy::new_without_default)]
510 pub const fn new() -> Self {
511 Self {
512 vtable: None,
513 k: None,
514 v: None,
515 }
516 }
517
518 pub const fn vtable(mut self, vtable: &'static MapVTable) -> Self {
520 self.vtable = Some(vtable);
521 self
522 }
523
524 pub const fn k(mut self, k: &'static Shape) -> Self {
526 self.k = Some(k);
527 self
528 }
529
530 pub const fn v(mut self, v: &'static Shape) -> Self {
532 self.v = Some(v);
533 self
534 }
535
536 pub const fn build(self) -> MapDef {
538 MapDef {
539 vtable: self.vtable.unwrap(),
540 k: self.k.unwrap(),
541 v: self.v.unwrap(),
542 }
543 }
544}
545
546#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
548#[non_exhaustive]
549pub struct ListDef {
550 pub vtable: &'static ListVTable,
552 pub t: &'static Shape,
554}
555
556impl ListDef {
557 pub const fn builder() -> ListDefBuilder {
559 ListDefBuilder::new()
560 }
561}
562
563pub struct ListDefBuilder {
565 vtable: Option<&'static ListVTable>,
566 t: Option<&'static Shape>,
567}
568
569impl ListDefBuilder {
570 #[allow(clippy::new_without_default)]
572 pub const fn new() -> Self {
573 Self {
574 vtable: None,
575 t: None,
576 }
577 }
578
579 pub const fn vtable(mut self, vtable: &'static ListVTable) -> Self {
581 self.vtable = Some(vtable);
582 self
583 }
584
585 pub const fn t(mut self, t: &'static Shape) -> Self {
587 self.t = Some(t);
588 self
589 }
590
591 pub const fn build(self) -> ListDef {
593 ListDef {
594 vtable: self.vtable.unwrap(),
595 t: self.t.unwrap(),
596 }
597 }
598}
599
600#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
602#[non_exhaustive]
603pub struct EnumDef {
604 pub repr: EnumRepr,
606 pub variants: &'static [Variant],
608}
609
610impl EnumDef {
611 pub const fn builder() -> EnumDefBuilder {
613 EnumDefBuilder::new()
614 }
615}
616
617pub struct EnumDefBuilder {
619 repr: Option<EnumRepr>,
620 variants: Option<&'static [Variant]>,
621}
622
623impl EnumDefBuilder {
624 #[allow(clippy::new_without_default)]
626 pub const fn new() -> Self {
627 Self {
628 repr: None,
629 variants: None,
630 }
631 }
632
633 pub const fn repr(mut self, repr: EnumRepr) -> Self {
635 self.repr = Some(repr);
636 self
637 }
638
639 pub const fn variants(mut self, variants: &'static [Variant]) -> Self {
641 self.variants = Some(variants);
642 self
643 }
644
645 pub const fn build(self) -> EnumDef {
647 EnumDef {
648 repr: self.repr.unwrap(),
649 variants: self.variants.unwrap(),
650 }
651 }
652}
653
654#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
656#[non_exhaustive]
657pub struct Variant {
658 pub name: &'static str,
660
661 pub discriminant: Option<i64>,
663
664 pub kind: VariantKind,
666}
667
668impl Variant {
669 pub const fn builder() -> VariantBuilder {
671 VariantBuilder::new()
672 }
673}
674
675pub struct VariantBuilder {
677 name: Option<&'static str>,
678 discriminant: Option<Option<i64>>,
679 kind: Option<VariantKind>,
680}
681
682impl VariantBuilder {
683 #[allow(clippy::new_without_default)]
685 pub const fn new() -> Self {
686 Self {
687 name: None,
688 discriminant: None,
689 kind: None,
690 }
691 }
692
693 pub const fn name(mut self, name: &'static str) -> Self {
695 self.name = Some(name);
696 self
697 }
698
699 pub const fn discriminant(mut self, discriminant: Option<i64>) -> Self {
701 self.discriminant = Some(discriminant);
702 self
703 }
704
705 pub const fn kind(mut self, kind: VariantKind) -> Self {
707 self.kind = Some(kind);
708 self
709 }
710
711 pub const fn build(self) -> Variant {
713 Variant {
714 name: self.name.unwrap(),
715 discriminant: self.discriminant.unwrap(),
716 kind: self.kind.unwrap(),
717 }
718 }
719}
720
721#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
723#[non_exhaustive]
724pub enum VariantKind {
725 Unit,
727
728 Tuple {
730 fields: &'static [Field],
732 },
733
734 Struct {
736 fields: &'static [Field],
738 },
739}
740
741#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
743#[non_exhaustive]
744pub enum EnumRepr {
745 Default,
747 U8,
749 U16,
751 U32,
753 U64,
755 USize,
757 I8,
759 I16,
761 I32,
763 I64,
765 ISize,
767}
768
769impl Default for EnumRepr {
770 fn default() -> Self {
771 Self::Default
772 }
773}
774
775#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
777#[non_exhaustive]
778pub struct ScalarDef {
779 pub type_id: ConstTypeId,
781}
782
783impl ScalarDef {
784 pub const fn of<T>() -> Self {
786 ScalarDefBuilder::new()
787 .type_id(ConstTypeId::of::<T>())
788 .build()
789 }
790}
791
792#[derive(Default)]
794pub struct ScalarDefBuilder {
795 type_id: Option<ConstTypeId>,
796}
797
798impl ScalarDefBuilder {
799 #[allow(clippy::new_without_default)]
801 pub const fn new() -> Self {
802 Self { type_id: None }
803 }
804
805 pub const fn type_id(mut self, type_id: ConstTypeId) -> Self {
807 self.type_id = Some(type_id);
808 self
809 }
810
811 pub const fn build(self) -> ScalarDef {
813 ScalarDef {
814 type_id: self.type_id.unwrap(),
815 }
816 }
817}
818
819#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
821#[non_exhaustive]
822pub enum Def {
823 Scalar(ScalarDef),
828
829 Struct(StructDef),
833
834 Map(MapDef),
838
839 List(ListDef),
843
844 Enum(EnumDef),
848}
849
850#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
852#[non_exhaustive]
853pub enum Characteristic {
854 Send,
857
858 Sync,
860
861 Copy,
863
864 Eq,
866
867 Clone,
870
871 Debug,
873
874 PartialEq,
876
877 PartialOrd,
879
880 Ord,
882
883 Hash,
885
886 Default,
888}
889
890impl Characteristic {
891 pub const fn all(self, shapes: &'static [&'static Shape]) -> bool {
893 let mut i = 0;
894 while i < shapes.len() {
895 if !shapes[i].is(self) {
896 return false;
897 }
898 i += 1;
899 }
900 true
901 }
902
903 pub const fn any(self, shapes: &'static [&'static Shape]) -> bool {
905 let mut i = 0;
906 while i < shapes.len() {
907 if shapes[i].is(self) {
908 return true;
909 }
910 i += 1;
911 }
912 false
913 }
914
915 pub const fn none(self, shapes: &'static [&'static Shape]) -> bool {
917 let mut i = 0;
918 while i < shapes.len() {
919 if shapes[i].is(self) {
920 return false;
921 }
922 i += 1;
923 }
924 true
925 }
926}