facet_core/_trait/
macros.rs

1use crate::{Facet, Shape};
2
3#[doc(hidden)]
4pub const fn shape_of<TStruct, TField: Facet>(_f: &dyn Fn(TStruct) -> TField) -> &'static Shape {
5    TField::SHAPE
6}
7
8#[doc(hidden)]
9#[macro_export]
10macro_rules! struct_field {
11    ($struct:ty, $field:tt) => {
12        $crate::Field::builder()
13            .name(stringify!($field))
14            .shape($crate::shape_of(&|s: $struct| s.$field))
15            .offset(::core::mem::offset_of!($struct, $field))
16            .flags($crate::FieldFlags::EMPTY)
17            .build()
18    };
19}
20
21#[doc(hidden)]
22#[macro_export]
23macro_rules! struct_fields {
24    ($struct:ty, ($($field:tt),*)) => {{
25        static FIELDS: &[$crate::Field] = &[ $($crate::struct_field!($struct, $field)),* ];
26        FIELDS
27    }};
28}
29
30#[doc(hidden)]
31#[macro_export]
32macro_rules! enum_unit_variant {
33    ($enum:ty, $variant:ident, $discriminant:expr) => {
34        $crate::Variant::builder()
35            .name(stringify!($variant))
36            .discriminant(Some($discriminant))
37            .kind($crate::VariantKind::Unit)
38            .build()
39    };
40}
41
42#[doc(hidden)]
43#[macro_export]
44macro_rules! enum_tuple_variant {
45    ($enum:ty, $variant:ident, [$(($field_type:ty, $offset:expr)),*], $discriminant:expr) => {{
46        static FIELDS: &[$crate::Field] = &[
47            $(
48                $crate::Field::builder()
49                    .name(concat!("_", stringify!($field_type)))
50                    .shape(<$field_type>::SHAPE)
51                    .offset($offset) // Explicit offset including discriminant
52                    .flags($crate::FieldFlags::EMPTY)
53                    .build()
54            ),*
55        ];
56
57        $crate::Variant::builder()
58            .name(stringify!($variant))
59            .discriminant(Some($discriminant))
60            .kind($crate::VariantKind::Tuple { fields: FIELDS })
61            .build()
62    }};
63}
64
65#[doc(hidden)]
66#[macro_export]
67macro_rules! enum_struct_variant {
68    ($enum:ty, $variant:ident, {$(($field:ident: $field_type:ty, $offset:expr)),*}, $discriminant:expr) => {{
69        static FIELDS: &[$crate::Field] = &[
70            $(
71                $crate::Field::builder()
72                    .name(stringify!($field))
73                    .shape(<$field_type>::SHAPE)
74                    .offset($offset) // Explicit offset including discriminant
75                    .flags($crate::FieldFlags::EMPTY)
76                    .build()
77            ),*
78        ];
79
80        $crate::Variant::builder()
81            .name(stringify!($variant))
82            .discriminant(Some($discriminant))
83            .kind($crate::VariantKind::Struct { fields: FIELDS })
84            .build()
85    }};
86}
87
88#[doc(hidden)]
89#[macro_export]
90macro_rules! enum_variants {
91    ($enum:ty, [$($variant:expr),*]) => {{
92        static VARIANTS: &[$crate::Variant] = &[ $($variant),* ];
93        VARIANTS
94    }};
95}
96
97/// Creates a `ValueVTable` for a given type.
98///
99/// This macro generates a `ValueVTable` with implementations for various traits
100/// (Display, Debug, PartialEq, Eq, PartialOrd, Ord, Hash) if they are implemented for the given type.
101///
102/// # Arguments
103///
104/// * `$type_name:ty` - The type for which to create the `ValueVTable`.
105/// * `$type_name_fn:expr` - A function that writes the type name to a formatter.
106///
107/// # Example
108///
109/// ```
110/// use facet_core::value_vtable;
111/// use core::fmt::{self, Formatter};
112/// use facet_core::TypeNameOpts;
113///
114/// let vtable = value_vtable!(String, |f: &mut Formatter<'_>, _opts: TypeNameOpts| write!(f, "String"));
115/// ```
116///
117/// This cannot be used for a generic type because the `impls!` thing depends on type bounds.
118/// If you have a generic type, you need to do specialization yourself, like we do for slices,
119/// arrays, etc. — essentially, this macro is only useful for 1) scalars, 2) inside a derive macro
120#[macro_export]
121macro_rules! value_vtable {
122    ($type_name:ty, $type_name_fn:expr) => {
123        &const {
124            let mut builder = $crate::ValueVTable::builder()
125                .type_name($type_name_fn)
126                .drop_in_place(|data| unsafe { data.drop_in_place::<$type_name>() });
127
128            if $crate::spez::impls!($type_name: core::fmt::Display) {
129                builder = builder.display(|data, f| {
130                    use $crate::spez::*;
131                    (&&Spez(unsafe { data.as_ref::<$type_name>() })).spez_display(f)
132                });
133            }
134
135            if $crate::spez::impls!($type_name: core::fmt::Debug) {
136                builder = builder.debug(|data, f| {
137                    use $crate::spez::*;
138                    (&&Spez(unsafe { data.as_ref::<$type_name>() })).spez_debug(f)
139                });
140            }
141
142            if $crate::spez::impls!($type_name: core::default::Default) {
143                builder = builder.default_in_place(|target| {
144                    use $crate::spez::*;
145                    unsafe { (&&SpezEmpty::<$type_name>::SPEZ).spez_default_in_place(target) }
146                });
147            }
148
149            if $crate::spez::impls!($type_name: core::clone::Clone) {
150                builder = builder.clone_into(|src, dst| {
151                    use $crate::spez::*;
152                    unsafe { (&&Spez(src.as_ref::<$type_name>())).spez_clone_into(dst) }
153                });
154            }
155
156            {
157                let mut traits = $crate::MarkerTraits::empty();
158                if $crate::spez::impls!($type_name: core::cmp::Eq) {
159                    traits = traits.union($crate::MarkerTraits::EQ);
160                }
161                if $crate::spez::impls!($type_name: core::marker::Send) {
162                    traits = traits.union($crate::MarkerTraits::SEND);
163                }
164                if $crate::spez::impls!($type_name: core::marker::Sync) {
165                    traits = traits.union($crate::MarkerTraits::SYNC);
166                }
167                if $crate::spez::impls!($type_name: core::marker::Copy) {
168                    traits = traits.union($crate::MarkerTraits::COPY);
169                }
170                builder = builder.marker_traits(traits);
171            }
172
173            if $crate::spez::impls!($type_name: core::cmp::PartialEq) {
174                builder = builder.eq(|left, right| {
175                    use $crate::spez::*;
176                    (&&Spez(unsafe { left.as_ref::<$type_name>() }))
177                        .spez_eq(&&Spez(unsafe { right.as_ref::<$type_name>() }))
178                });
179            }
180
181            if $crate::spez::impls!($type_name: core::cmp::PartialOrd) {
182                builder = builder.partial_ord(|left, right| {
183                    use $crate::spez::*;
184                    (&&Spez(unsafe { left.as_ref::<$type_name>() }))
185                        .spez_partial_cmp(&&Spez(unsafe { right.as_ref::<$type_name>() }))
186                });
187            }
188
189            if $crate::spez::impls!($type_name: core::cmp::Ord) {
190                builder = builder.ord(|left, right| {
191                    use $crate::spez::*;
192                    (&&Spez(unsafe { left.as_ref::<$type_name>() }))
193                        .spez_cmp(&&Spez(unsafe { right.as_ref::<$type_name>() }))
194                });
195            }
196
197            if $crate::spez::impls!($type_name: core::hash::Hash) {
198                builder = builder.hash(|value, hasher_this, hasher_write_fn| {
199                    use $crate::spez::*;
200                    use $crate::HasherProxy;
201                    (&&Spez(unsafe { value.as_ref::<$type_name>() }))
202                        .spez_hash(&mut unsafe { HasherProxy::new(hasher_this, hasher_write_fn) })
203                });
204            }
205
206            if $crate::spez::impls!($type_name: core::str::FromStr) {
207                builder = builder.parse(|s, target| {
208                    use $crate::spez::*;
209                    let res = unsafe { (&&SpezEmpty::<$type_name>::SPEZ).spez_parse(s, target) };
210                    res.map(|_| unsafe { target.assume_init() })
211                });
212            }
213
214            builder.build()
215        }
216    };
217}