facet_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 {
13            name: stringify!($field),
14            shape: $crate::shape_of(&|s: $struct| s.$field),
15            offset: ::std::mem::offset_of!($struct, $field),
16            flags: $crate::FieldFlags::EMPTY,
17        }
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) => {
34        $crate::Variant {
35            name: stringify!($variant),
36            discriminant: None,
37            kind: $crate::VariantKind::Unit,
38        }
39    };
40    ($enum:ty, $variant:ident, $discriminant:expr) => {
41        $crate::Variant {
42            name: stringify!($variant),
43            discriminant: Some($discriminant),
44            kind: $crate::VariantKind::Unit,
45        }
46    };
47}
48
49#[doc(hidden)]
50#[macro_export]
51macro_rules! enum_tuple_variant {
52    ($enum:ty, $variant:ident, [$($field_type:ty),*]) => {{
53        static FIELDS: &[$crate::Field] = &[
54            $(
55                $crate::Field {
56                    name: concat!("_", stringify!($field_type)),
57                    shape_fn: <$field_type>::shape,
58                    offset: 0, // Will be calculated at runtime
59                    flags: $crate::FieldFlags::EMPTY,
60                }
61            ),*
62        ];
63
64        $crate::Variant {
65            name: stringify!($variant),
66            discriminant: None,
67            kind: $crate::VariantKind::Tuple { fields: FIELDS },
68        }
69    }};
70    ($enum:ty, $variant:ident, [$($field_type:ty),*], $discriminant:expr) => {{
71        static FIELDS: &[$crate::Field] = &[
72            $(
73                $crate::Field {
74                    name: concat!("_", stringify!($field_type)),
75                    shape_fn: <$field_type>::shape,
76                    offset: 0, // Will be calculated at runtime
77                    flags: $crate::FieldFlags::EMPTY,
78                }
79            ),*
80        ];
81
82        $crate::Variant {
83            name: stringify!($variant),
84            discriminant: Some($discriminant),
85            kind: $crate::VariantKind::Tuple { fields: FIELDS },
86        }
87    }};
88}
89
90#[doc(hidden)]
91#[macro_export]
92macro_rules! enum_struct_variant {
93    ($enum:ty, $variant:ident, {$($field:ident: $field_type:ty),*}) => {{
94        static FIELDS: &[$crate::Field] = &[
95            $(
96                $crate::Field {
97                    name: stringify!($field),
98                    shape_fn: <$field_type>::shape,
99                    offset: 0, // Will be calculated at runtime
100                    flags: $crate::FieldFlags::EMPTY,
101                }
102            ),*
103        ];
104
105        $crate::Variant {
106            name: stringify!($variant),
107            discriminant: None,
108            kind: $crate::VariantKind::Struct { fields: FIELDS },
109        }
110    }};
111    ($enum:ty, $variant:ident, {$($field:ident: $field_type:ty),*}, $discriminant:expr) => {{
112        static FIELDS: &[$crate::Field] = &[
113            $(
114                $crate::Field {
115                    name: stringify!($field),
116                    shape_fn: <$field_type>::shape,
117                    offset: 0, // Will be calculated at runtime
118                    flags: $crate::FieldFlags::EMPTY,
119                }
120            ),*
121        ];
122
123        $crate::Variant {
124            name: stringify!($variant),
125            discriminant: Some($discriminant),
126            kind: $crate::VariantKind::Struct { fields: FIELDS },
127        }
128    }};
129}
130
131#[doc(hidden)]
132#[macro_export]
133macro_rules! enum_variants {
134    ($enum:ty, [$($variant:expr),*]) => {{
135        static VARIANTS: &[$crate::Variant] = &[ $($variant),* ];
136        VARIANTS
137    }};
138}
139
140/// Creates a `ValueVTable` for a given type.
141///
142/// This macro generates a `ValueVTable` with implementations for various traits
143/// (Display, Debug, PartialEq, Eq, PartialOrd, Ord, Hash) if they are implemented for the given type.
144///
145/// # Arguments
146///
147/// * `$type_name:ty` - The type for which to create the `ValueVTable`.
148/// * `$type_name_fn:expr` - A function that writes the type name to a formatter.
149///
150/// # Example
151///
152/// ```
153/// use facet_trait::value_vtable;
154/// use std::fmt::{self, Formatter};
155/// use facet_types::TypeNameOpts;
156///
157/// let vtable = value_vtable!(String, |f: &mut Formatter<'_>, _opts: TypeNameOpts| write!(f, "String"));
158/// ```
159///
160/// This cannot be used for a generic type because the `impls!` thing depends on type bounds.
161/// If you have a generic type, you need to do specialization yourself, like we do for slices,
162/// arrays, etc. — essentially, this macro is only useful for 1) scalars, 2) inside a derive macro
163#[macro_export]
164macro_rules! value_vtable {
165    ($type_name:ty, $type_name_fn:expr) => {
166        &$crate::ValueVTable {
167            type_name: $type_name_fn,
168            display: if $crate::facet_spez::impls!($type_name: std::fmt::Display) {
169                Some(|data, f| {
170                    use $crate::facet_spez::*;
171                    (&&Spez(unsafe { data.as_ref::<$type_name>() })).spez_display(f)
172                })
173            } else {
174                None
175            },
176            debug: if $crate::facet_spez::impls!($type_name: std::fmt::Debug) {
177                Some(|data, f| {
178                    use $crate::facet_spez::*;
179                    (&&Spez(unsafe { data.as_ref::<$type_name>() })).spez_debug(f)
180                })
181            } else {
182                None
183            },
184            default_in_place: if $crate::facet_spez::impls!($type_name: std::default::Default) {
185                Some(|target| {
186                    use $crate::facet_spez::*;
187                    (&&Spez(<$type_name as $crate::Facet>::DUMMY)).spez_default_in_place(target)
188                })
189            } else {
190                None
191            },
192            clone_into: if $crate::facet_spez::impls!($type_name: std::clone::Clone) {
193                Some(|src, dst| {
194                    use $crate::facet_spez::*;
195                    (&&Spez(unsafe { src.as_ref::<$type_name>() })).spez_clone_into(dst)
196                })
197            } else {
198                None
199            },
200            marker_traits: {
201                const fn combine_traits() -> $crate::MarkerTraits {
202                    let mut traits = $crate::MarkerTraits::empty();
203                    if $crate::facet_spez::impls!($type_name: std::cmp::Eq) {
204                        traits = traits.union($crate::MarkerTraits::EQ);
205                    }
206                    if $crate::facet_spez::impls!($type_name: std::marker::Send) {
207                        traits = traits.union($crate::MarkerTraits::SEND);
208                    }
209                    if $crate::facet_spez::impls!($type_name: std::marker::Sync) {
210                        traits = traits.union($crate::MarkerTraits::SYNC);
211                    }
212                    if $crate::facet_spez::impls!($type_name: std::marker::Copy) {
213                        traits = traits.union($crate::MarkerTraits::COPY);
214                    }
215                    traits
216                }
217                combine_traits()
218            },
219            eq: if $crate::facet_spez::impls!($type_name: std::cmp::PartialEq) {
220                Some(|left, right| {
221                    use $crate::facet_spez::*;
222                    (&&Spez(unsafe { left.as_ref::<$type_name>() }))
223                        .spez_eq(&&Spez(unsafe { right.as_ref::<$type_name>() }))
224                })
225            } else {
226                None
227            },
228            partial_ord: if $crate::facet_spez::impls!($type_name: std::cmp::PartialOrd) {
229                Some(|left, right| {
230                    use $crate::facet_spez::*;
231                    (&&Spez(unsafe { left.as_ref::<$type_name>() }))
232                        .spez_partial_cmp(&&Spez(unsafe { right.as_ref::<$type_name>() }))
233                })
234            } else {
235                None
236            },
237            ord: if $crate::facet_spez::impls!($type_name: std::cmp::Ord) {
238                Some(|left, right| {
239                    use $crate::facet_spez::*;
240                    (&&Spez(unsafe { left.as_ref::<$type_name>() }))
241                        .spez_cmp(&&Spez(unsafe { right.as_ref::<$type_name>() }))
242                })
243            } else {
244                None
245            },
246            hash: if $crate::facet_spez::impls!($type_name: std::hash::Hash) {
247                Some(|value, hasher_this, hasher_write_fn| {
248                    use $crate::facet_spez::*;
249                    use $crate::HasherProxy;
250                    (&&Spez(unsafe { value.as_ref::<$type_name>() }))
251                        .spez_hash(&mut unsafe { HasherProxy::new(hasher_this, hasher_write_fn) })
252                })
253            } else {
254                None
255            },
256            drop_in_place: Some(|data| unsafe { data.drop_in_place::<$type_name>() }),
257            parse: None,
258            try_from: None,
259        }
260    };
261}