facet_core/
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)]
9pub const fn shape_of_opaque<TStruct, TField>(_f: &dyn Fn(&TStruct) -> &TField) -> &'static Shape
10where
11    Opaque<TField>: Facet,
12{
13    Opaque::<TField>::SHAPE
14}
15
16/// Helper type for opaque members
17pub struct Opaque<T>(T);
18
19/// Creates a `ValueVTable` for a given type.
20///
21/// This macro generates a `ValueVTable` with implementations for various traits
22/// (Display, Debug, PartialEq, Eq, PartialOrd, Ord, Hash) if they are implemented for the given type.
23///
24/// # Arguments
25///
26/// * `$type_name:ty` - The type for which to create the `ValueVTable`.
27/// * `$type_name_fn:expr` - A function that writes the type name to a formatter.
28///
29/// # Example
30///
31/// ```
32/// use facet_core::value_vtable;
33/// use core::fmt::{self, Formatter};
34/// use facet_core::TypeNameOpts;
35///
36/// let vtable = value_vtable!(String, |f: &mut Formatter<'_>, _opts: TypeNameOpts| write!(f, "String"));
37/// ```
38///
39/// This cannot be used for a generic type because the `impls!` thing depends on type bounds.
40/// If you have a generic type, you need to do specialization yourself, like we do for slices,
41/// arrays, etc. — essentially, this macro is only useful for 1) scalars, 2) inside a derive macro
42#[macro_export]
43macro_rules! value_vtable {
44    ($type_name:ty, $type_name_fn:expr) => {
45        &$crate::value_vtable_inner!($type_name, $type_name_fn)
46    };
47}
48
49/// Creates a `ValueVTable` for a given type, see [`value_vtable!`] for more details.
50#[macro_export]
51macro_rules! value_vtable_inner {
52    ($type_name:ty, $type_name_fn:expr) => {
53        const {
54            let mut builder = $crate::ValueVTable::builder()
55                .type_name($type_name_fn)
56                .drop_in_place(|data| unsafe { data.drop_in_place::<$type_name>() });
57
58            if $crate::spez::impls!($type_name: core::fmt::Display) {
59                builder = builder.display(|data, f| {
60                    use $crate::spez::*;
61                    (&&Spez(unsafe { data.get::<$type_name>() })).spez_display(f)
62                });
63            }
64
65            if $crate::spez::impls!($type_name: core::fmt::Debug) {
66                builder = builder.debug(|data, f| {
67                    use $crate::spez::*;
68                    (&&Spez(unsafe { data.get::<$type_name>() })).spez_debug(f)
69                });
70            }
71
72            if $crate::spez::impls!($type_name: core::default::Default) {
73                builder = builder.default_in_place(|target| {
74                    use $crate::spez::*;
75                    unsafe { (&&SpezEmpty::<$type_name>::SPEZ).spez_default_in_place(target) }
76                });
77            }
78
79            if $crate::spez::impls!($type_name: core::clone::Clone) {
80                builder = builder.clone_into(|src, dst| {
81                    use $crate::spez::*;
82                    unsafe { (&&Spez(src.get::<$type_name>())).spez_clone_into(dst) }
83                });
84            }
85
86            {
87                let mut traits = $crate::MarkerTraits::empty();
88                if $crate::spez::impls!($type_name: core::cmp::Eq) {
89                    traits = traits.union($crate::MarkerTraits::EQ);
90                }
91                if $crate::spez::impls!($type_name: core::marker::Send) {
92                    traits = traits.union($crate::MarkerTraits::SEND);
93                }
94                if $crate::spez::impls!($type_name: core::marker::Sync) {
95                    traits = traits.union($crate::MarkerTraits::SYNC);
96                }
97                if $crate::spez::impls!($type_name: core::marker::Copy) {
98                    traits = traits.union($crate::MarkerTraits::COPY);
99                }
100                if $crate::spez::impls!($type_name: core::marker::Unpin) {
101                    traits = traits.union($crate::MarkerTraits::UNPIN);
102                }
103                builder = builder.marker_traits(traits);
104            }
105
106            if $crate::spez::impls!($type_name: core::cmp::PartialEq) {
107                builder = builder.eq(|left, right| {
108                    use $crate::spez::*;
109                    (&&Spez(unsafe { left.get::<$type_name>() }))
110                        .spez_eq(&&Spez(unsafe { right.get::<$type_name>() }))
111                });
112            }
113
114            if $crate::spez::impls!($type_name: core::cmp::PartialOrd) {
115                builder = builder.partial_ord(|left, right| {
116                    use $crate::spez::*;
117                    (&&Spez(unsafe { left.get::<$type_name>() }))
118                        .spez_partial_cmp(&&Spez(unsafe { right.get::<$type_name>() }))
119                });
120            }
121
122            if $crate::spez::impls!($type_name: core::cmp::Ord) {
123                builder = builder.ord(|left, right| {
124                    use $crate::spez::*;
125                    (&&Spez(unsafe { left.get::<$type_name>() }))
126                        .spez_cmp(&&Spez(unsafe { right.get::<$type_name>() }))
127                });
128            }
129
130            if $crate::spez::impls!($type_name: core::hash::Hash) {
131                builder = builder.hash(|value, hasher_this, hasher_write_fn| {
132                    use $crate::spez::*;
133                    use $crate::HasherProxy;
134                    (&&Spez(unsafe { value.get::<$type_name>() }))
135                        .spez_hash(&mut unsafe { HasherProxy::new(hasher_this, hasher_write_fn) })
136                });
137            }
138
139            if $crate::spez::impls!($type_name: core::str::FromStr) {
140                builder = builder.parse(|s, target| {
141                    use $crate::spez::*;
142                    let res = unsafe { (&&SpezEmpty::<$type_name>::SPEZ).spez_parse(s, target) };
143                    res.map(|_| unsafe { target.assume_init() })
144                });
145            }
146
147            builder.build()
148        }
149    };
150}