facet_core/
macros.rs

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