facet_core/
macros.rs

1use crate::{Facet, Opaque, Shape};
2
3/// Helper for the derive macro to infer the shape of a struct field.
4///
5/// This function is never actually called at runtime — it exists purely to let
6/// the compiler infer `TField` from a field accessor closure like `|s: &MyStruct| &s.field`.
7/// By passing a reference to that closure, the compiler resolves `TField` and we can
8/// return `TField::SHAPE` at compile time.
9#[doc(hidden)]
10pub const fn shape_of<'facet, TStruct, TField: Facet<'facet>>(
11    _f: &dyn Fn(&TStruct) -> &TField,
12) -> &'static Shape {
13    TField::SHAPE
14}
15
16/// Helper for the derive macro to infer the shape of an opaque struct field.
17///
18/// Similar to [`shape_of`], but wraps the field type in [`Opaque`] for types that
19/// don't implement `Facet` directly. The closure `|s: &MyStruct| &s.field` lets the
20/// compiler infer `TField`, and we return `Opaque::<TField>::SHAPE`.
21#[doc(hidden)]
22pub const fn shape_of_opaque<'a, TStruct, TField>(
23    _f: &dyn Fn(&TStruct) -> &TField,
24) -> &'static Shape
25where
26    Opaque<TField>: Facet<'a>,
27{
28    Opaque::<TField>::SHAPE
29}
30
31/// Helper for the derive macro to infer the source shape for `#[facet(deserialize_with = ...)]`.
32///
33/// Given a `deserialize_with` function like `fn(&Source) -> Result<Target, E>`, this helper
34/// infers `Source` from the function signature and returns `Source::SHAPE`. This shape is
35/// stored as a [`FieldAttribute::DeserializeFrom`] so the reflection system knows what
36/// intermediate type to construct before calling the conversion function.
37#[doc(hidden)]
38pub const fn shape_of_deserialize_with_source<'facet, Source: Facet<'facet>, Target>(
39    _f: &dyn Fn(&Source) -> Target,
40) -> &'static Shape {
41    Source::SHAPE
42}
43
44/// Helper for the derive macro to infer the target shape for `#[facet(serialize_with = ...)]`.
45///
46/// Given a `serialize_with` function like `fn(&Source) -> Result<Target, &'static str>`, this
47/// helper infers `Target` from the function signature and returns `Target::SHAPE`. This shape
48/// is stored as a [`FieldAttribute::SerializeInto`] so the reflection system knows what
49/// type to serialize instead of the original field type.
50#[doc(hidden)]
51pub const fn shape_of_serialize_with_target<'facet, Source, Target: Facet<'facet>>(
52    _f: &dyn Fn(&Source) -> Result<Target, &'static str>,
53) -> &'static Shape {
54    Target::SHAPE
55}
56
57/// Creates a `ValueVTable` for a given type.
58///
59/// This macro generates a `ValueVTable` with implementations for various traits
60/// (Display, Debug, PartialEq, PartialOrd, Ord, Hash) if they are implemented for the given type.
61///
62/// # Arguments
63///
64/// * `$type_name:ty` - The type for which to create the `ValueVTable`.
65/// * `$type_name_fn:expr` - A function that writes the type name to a formatter.
66///
67/// # Example
68///
69/// ```
70/// use facet_core::value_vtable;
71/// use core::fmt::{self, Formatter};
72/// use facet_core::TypeNameOpts;
73///
74/// let vtable = value_vtable!(String, |f: &mut Formatter<'_>, _opts: TypeNameOpts| write!(f, "String"));
75/// ```
76///
77/// This cannot be used for a generic type because the `impls!` thing depends on type bounds.
78/// If you have a generic type, you need to do specialization yourself, like we do for slices,
79/// arrays, etc. — essentially, this macro is only useful for 1) scalars, 2) inside a derive macro
80#[macro_export]
81macro_rules! value_vtable {
82    ($type_name:ty, $type_name_fn:expr $(,)?) => {
83        const {
84            $crate::ValueVTable::builder::<$type_name>()
85                .type_name($type_name_fn)
86                .display({
87                    if $crate::spez::impls!($type_name: core::fmt::Display) {
88                        Some(|data: $crate::TypedPtrConst<'_, _>, f| {
89                            let data = data.get();
90                            use $crate::spez::*;
91                            (&&Spez(data)).spez_display(f)
92                        })
93                    } else {
94                        None
95                    }
96                })
97                .debug({
98                    if $crate::spez::impls!($type_name: core::fmt::Debug) {
99                        Some(|data: $crate::TypedPtrConst<'_, _>, f| {
100                            let data = data.get();
101                            use $crate::spez::*;
102                            (&&Spez(data)).spez_debug(f)
103                        })
104                    } else {
105                        None
106                    }
107                })
108                .default_in_place({
109                    if $crate::spez::impls!($type_name: core::default::Default) {
110                        Some(|target: $crate::TypedPtrUninit<'_, _>| unsafe {
111                            use $crate::spez::*;
112                            $crate::TypedPtrMut::new((&&SpezEmpty::<$type_name>::SPEZ).spez_default_in_place(target.into()).as_mut())
113                        })
114                    } else {
115                        None
116                    }
117                })
118                .clone_into({
119                    if $crate::spez::impls!($type_name: core::clone::Clone) {
120                        Some(|src: $crate::TypedPtrConst<'_, _>, dst: $crate::TypedPtrUninit<'_, _>| unsafe {
121                            use $crate::spez::*;
122                            let src = src.get();
123                            $crate::TypedPtrMut::new((&&Spez(src)).spez_clone_into(dst.into()).as_mut())
124                        })
125                    } else {
126                        None
127                    }
128                })
129                .marker_traits({
130                    let mut traits = $crate::MarkerTraits::empty();
131                    if $crate::spez::impls!($type_name: core::cmp::Eq) {
132                        traits = traits.union($crate::MarkerTraits::EQ);
133                    }
134                    if $crate::spez::impls!($type_name: core::marker::Send) {
135                        traits = traits.union($crate::MarkerTraits::SEND);
136                    }
137                    if $crate::spez::impls!($type_name: core::marker::Sync) {
138                        traits = traits.union($crate::MarkerTraits::SYNC);
139                    }
140                    if $crate::spez::impls!($type_name: core::marker::Copy) {
141                        traits = traits.union($crate::MarkerTraits::COPY);
142                    }
143                    if $crate::spez::impls!($type_name: core::marker::Unpin) {
144                        traits = traits.union($crate::MarkerTraits::UNPIN);
145                    }
146                    if $crate::spez::impls!($type_name: core::panic::UnwindSafe) {
147                        traits = traits.union($crate::MarkerTraits::UNWIND_SAFE);
148                    }
149                    if $crate::spez::impls!($type_name: core::panic::RefUnwindSafe) {
150                        traits = traits.union($crate::MarkerTraits::REF_UNWIND_SAFE);
151                    }
152
153                    traits
154                })
155                .partial_eq({
156                    if $crate::spez::impls!($type_name: core::cmp::PartialEq) {
157                        Some(|left: $crate::TypedPtrConst<'_, _>, right: $crate::TypedPtrConst<'_, _>| {
158                            let left = left.get();
159                            let right = right.get();
160                            use $crate::spez::*;
161                            (&&Spez(left))
162                                .spez_partial_eq(&&Spez(right))
163                        })
164                    } else {
165                        None
166                    }
167                })
168                .partial_ord({
169                    if $crate::spez::impls!($type_name: core::cmp::PartialOrd) {
170                        Some(|left: $crate::TypedPtrConst<'_, _>, right: $crate::TypedPtrConst<'_, _>| {
171                            let left = left.get();
172                            let right = right.get();
173                            use $crate::spez::*;
174                            (&&Spez(left))
175                                .spez_partial_cmp(&&Spez(right))
176                        })
177                    } else {
178                        None
179                    }
180                })
181                .ord({
182                    if $crate::spez::impls!($type_name: core::cmp::Ord) {
183                        Some(|left: $crate::TypedPtrConst<'_, _>, right: $crate::TypedPtrConst<'_, _>| {
184                            let left = left.get();
185                            let right = right.get();
186                            use $crate::spez::*;
187                            (&&Spez(left))
188                                .spez_cmp(&&Spez(right))
189                        })
190                    } else {
191                        None
192                    }
193                })
194                .hash({
195                    if $crate::spez::impls!($type_name: core::hash::Hash) {
196                        Some(|value: $crate::TypedPtrConst<'_, _>, hasher| {
197                            let value = value.get();
198                            use $crate::spez::*;
199                            (&&Spez(value))
200                                .spez_hash(&mut { hasher })
201                        })
202                    } else {
203                        None
204                    }
205                })
206                .parse({
207                    if $crate::spez::impls!($type_name: core::str::FromStr) {
208                        Some(|s, target: $crate::TypedPtrUninit<'_, _>| {
209                            use $crate::spez::*;
210                            let res = unsafe { (&&SpezEmpty::<$type_name>::SPEZ).spez_parse(s, target.into()) };
211                            res.map(|res| unsafe { $crate::TypedPtrMut::new(res.as_mut()) })
212                        })
213                    } else {
214                        None
215                    }
216                })
217                .build()
218        }
219    };
220}
221
222/// Creates a `ShapeBuilder` for a given type.
223#[macro_export]
224macro_rules! shape_builder {
225    ($type_name:ty $(,)?) => {
226        const {
227            use $crate::spez::*;
228            SpezEmpty::<$type_name>::BUILDER
229        }
230    };
231}