late_struct/
traits.rs

1use crate::{
2    LateFieldDescriptor, LateStructDescriptor, RawLateFieldDescriptor, RawLateStructDescriptor,
3};
4
5// === Trait Definitions === //
6
7/// A trait for a marker type representing a late-initialized structure.
8///
9/// This trait cannot be implemented manually. Instead, it should be implemented using the
10/// [`late_struct!`](crate::late_struct) macro. Most documentation for this trait can be found there.
11///
12/// ## Safety
13///
14/// This trait can only be safely implemented through the `late_struct!` macro.
15///
16pub unsafe trait LateStruct: Sized + 'static + LateStructSealed {
17    /// The type all fields are expected to coerce into. This is usually a [trait object].
18    ///
19    /// In the following invocation of [`late_struct!`](crate::late_struct)...
20    ///
21    /// ```
22    /// # use std::any::Any;
23    /// use late_struct::late_struct;
24    /// # pub struct MyType;
25    /// late_struct!(MyType => dyn Any + Send + Sync);
26    /// ```
27    ///
28    /// ...the type `<MyType as LateStruct>::EraseTo` would be `dyn Any + Send + Sync`.
29    ///
30    /// If no erase-to type is specified in an invocation of `late_struct!`, a default
31    /// `dyn 'static + fmt::Debug` type will be used.
32    ///
33    /// [trait object]: https://doc.rust-lang.org/1.87.0/reference/types/trait-object.html#r-type.trait-object
34    type EraseTo: ?Sized + 'static;
35
36    /// Fetches the untyped descriptor associated with this structure.
37    ///
38    /// See [`LateStruct::descriptor`] for a typed version of this method.
39    fn raw_descriptor() -> &'static RawLateStructDescriptor;
40
41    /// Fetches the strongly-typed descriptor associated with this structure.
42    fn descriptor() -> &'static LateStructDescriptor<Self> {
43        unsafe { Self::raw_descriptor().typed_unchecked() }
44    }
45}
46
47/// A trait for a marker type representing a field in a late-initialized structure.
48///
49/// This trait cannot be implemented manually. Instead, it should be implemented using the
50/// [`late_field!`](crate::late_field) macro. Most documentation for this trait can be found there.
51///
52/// ## Safety
53///
54/// This trait can only be safely implemented through the `late_field!` macro.
55///
56pub unsafe trait LateField<S: LateStruct>: Sized + 'static + LateFieldSealed<S> {
57    /// The type of this field's value.
58    type Value: 'static + Default;
59
60    /// Fetches the untyped descriptor associated with this field.
61    ///
62    /// See [`LateField::descriptor`] for a typed version of this method.
63    fn raw_descriptor() -> &'static RawLateFieldDescriptor;
64
65    /// Fetches the strongly-typed descriptor associated with this field.
66    fn descriptor() -> &'static LateFieldDescriptor<S> {
67        unsafe { Self::raw_descriptor().typed_unchecked() }
68    }
69
70    /// Coerces a pointer to the field's concrete value to its unsized form expected used in
71    /// [`LateStruct::EraseTo`].
72    fn coerce(value: *mut Self::Value) -> *mut S::EraseTo;
73}
74
75// === Sealed Traits === //
76
77mod sealed {
78    use super::LateStruct;
79
80    #[doc(hidden)]
81    pub trait LateStructSealed {}
82
83    #[doc(hidden)]
84    pub trait LateFieldSealed<S: LateStruct> {}
85}
86
87pub(crate) use sealed::{LateFieldSealed, LateStructSealed};
88
89// === Implementation Macros === //
90
91#[doc(hidden)]
92pub mod late_macro_internals {
93    use std::{any::TypeId, fmt, marker::PhantomData};
94
95    // === Re-exports === //
96
97    pub use {
98        super::sealed::{LateFieldSealed, LateStructSealed},
99        crate::{
100            LateField, LateStruct, RawLateFieldDescriptor, RawLateStructDescriptor, late_field,
101            late_struct,
102        },
103    };
104
105    pub type DefaultEraseTo = dyn 'static + fmt::Debug;
106
107    pub const fn new_late_struct_descriptor<S: LateStruct>() -> RawLateStructDescriptor {
108        RawLateStructDescriptor::new::<S>()
109    }
110
111    pub const fn new_late_field_descriptor<S, F>() -> RawLateFieldDescriptor
112    where
113        S: LateStruct,
114        F: LateField<S>,
115    {
116        RawLateFieldDescriptor::new::<S, F>()
117    }
118
119    // === OrDefault === //
120
121    pub type OrDefault<Default, T = Default> =
122        <OrDefaultHelper<T, Default> as OrDefaultHelperTrait>::Out;
123
124    pub struct OrDefaultHelper<T, Ignore>(PhantomData<T>, PhantomData<Ignore>)
125    where
126        T: ?Sized,
127        Ignore: ?Sized;
128
129    pub trait OrDefaultHelperTrait {
130        type Out: ?Sized;
131    }
132
133    impl<T, Ignore> OrDefaultHelperTrait for OrDefaultHelper<T, Ignore>
134    where
135        T: ?Sized,
136        Ignore: ?Sized,
137    {
138        type Out = T;
139    }
140
141    // === Entry management === //
142
143    #[derive(Debug, Copy, Clone)]
144    pub struct LateStructEntry {
145        pub struct_type: TypeId,
146        pub descriptor: &'static RawLateStructDescriptor,
147    }
148
149    impl LateStructEntry {
150        pub fn of<S: LateStruct>() -> Self {
151            Self {
152                struct_type: TypeId::of::<S>(),
153                descriptor: S::raw_descriptor(),
154            }
155        }
156    }
157
158    #[derive(Debug, Copy, Clone)]
159    pub struct LateFieldEntry {
160        pub struct_type: TypeId,
161        pub descriptor: &'static RawLateFieldDescriptor,
162    }
163
164    impl LateFieldEntry {
165        pub fn of<S: LateStruct, F: LateField<S>>() -> Self {
166            Self {
167                struct_type: TypeId::of::<S>(),
168                descriptor: F::raw_descriptor(),
169            }
170        }
171    }
172
173    cfg_if::cfg_if! {
174        if #[cfg(target_family = "wasm")] {
175            pub use ::inventory;
176
177            pub struct LateStructEntryWrapper(pub fn() -> LateStructEntry);
178            pub struct LateFieldEntryWrapper(pub fn() -> LateFieldEntry);
179
180            inventory::collect!(LateStructEntryWrapper);
181            inventory::collect!(LateFieldEntryWrapper);
182
183            pub fn iter_late_structs() -> impl Iterator<Item = LateStructEntry> {
184                inventory::iter::<LateStructEntryWrapper>().map(|f| f.0())
185            }
186
187            pub fn iter_late_fields() -> impl Iterator<Item = LateFieldEntry> {
188                inventory::iter::<LateFieldEntryWrapper>().map(|f| f.0())
189            }
190        } else {
191            pub use ::linkme;
192
193            #[linkme::distributed_slice]
194            pub static LATE_STRUCTS: [fn() -> LateStructEntry];
195
196            #[linkme::distributed_slice]
197            pub static LATE_FIELDS: [fn() -> LateFieldEntry];
198
199            pub fn iter_late_structs() -> impl Iterator<Item = LateStructEntry> {
200                LATE_STRUCTS.iter().map(|&f| f())
201            }
202
203            pub fn iter_late_fields() -> impl Iterator<Item = LateFieldEntry> {
204                LATE_FIELDS.iter().map(|&f| f())
205            }
206        }
207    }
208}
209
210/// Implements the [`LateStruct`] trait for the specified `$ty` type, turing it into a marker type
211/// that can be used to refer to a late-initialized structure.
212///
213/// See the [crate level documentation](crate) for examples of this macro in action as well as a
214/// step-by-step guide on how to use it.
215///
216/// You can attach fields onto this type using the [`late_field!`](crate::late_field) macro and can
217/// instantiate the structure defined by this macro using the [`LateInstance`](super::LateInstance)
218/// struct. Either operation can occur anywhere within the crate graph of a project; fields can be
219/// added to the structure in crates which are downstream to crates which instantiate that
220/// structure.
221///
222/// The `$ty` type need only be [`Sized`] and live for `'static`. No types in this macro invocation
223/// may involve unbound generic parameters.
224///
225/// You can specify more than one type onto which `LateStruct` should be implemented with this
226/// macro.
227///
228/// ## The `EraseTo` Parameter
229///
230/// The optional `$erase_to` type specifies the type all field values should be able to upcast (i.e.
231/// "erase") to. Generally, this type is a [trait object]. For instance, if you specify `dyn Any +
232/// Send + Sync`, all field values will have to implement [`Any`](std::any::Any), [`Send`], and
233/// [`Sync`]. If `$erase_to` is omitted, the type will default to `dyn 'static + fmt::Debug`, which
234/// would entail that all fields in the struct must implement the [`Debug`](std::fmt::Debug) trait.
235///
236/// In addition to the trait constraints `$erased_to` places on fields, `late_field!` automatically
237/// enforces that all fields live for `'static`, be [`Sized`], and implement the [`Default`] trait.
238/// It is this `Default` trait implementation which is used to initialize instances of this struct.
239///
240/// Traits such as [`Eq`], [`Clone`], and [`Hash`] are not [`dyn` compatible] and thus cannot be
241/// used directly inside the bounds of the `$erase_to` type. You can work around this restriction by
242/// using the [`DynEq`](super::DynEq), [`DynClone`](super::DynClone), and
243/// [`DynHash`](super::DynHash) traits respectively, which are `dyn` compatible while still encoding
244/// the necessary constraints.
245///
246/// [trait object]: https://doc.rust-lang.org/1.87.0/reference/types/trait-object.html#r-type.trait-object
247/// [`dyn` compatible]: https://doc.rust-lang.org/1.87.0/reference/items/traits.html#r-items.traits.dyn-compatible
248#[macro_export]
249macro_rules! late_struct {
250    (
251        $($ty:ty $(=> $erase_to:ty)?),*
252        $(,)?
253    ) => {$(
254        const _: () = {
255            static DESCRIPTOR: $crate::late_macro_internals::RawLateStructDescriptor =
256                $crate::late_macro_internals::new_late_struct_descriptor::<$ty>();
257
258            impl $crate::late_macro_internals::LateStructSealed for $ty {}
259
260            unsafe impl $crate::late_macro_internals::LateStruct for $ty {
261                type EraseTo = $crate::late_macro_internals::OrDefault<
262                    $crate::late_macro_internals::DefaultEraseTo
263                    $(, $erase_to)?
264                >;
265
266                fn raw_descriptor() -> &'static $crate::late_macro_internals::RawLateStructDescriptor {
267                    &DESCRIPTOR
268                }
269            }
270
271            #[cfg(target_family = "wasm")]
272            $crate::late_macro_internals::inventory::submit! {
273                $crate::late_macro_internals::LateStructEntryWrapper(
274                    $crate::late_macro_internals::LateStructEntry::of::<$ty>,
275                )
276            }
277
278            #[cfg(not(target_family = "wasm"))]
279            #[$crate::late_macro_internals::linkme::distributed_slice(
280                $crate::late_macro_internals::LATE_STRUCTS
281            )]
282            #[linkme(crate = $crate::late_macro_internals::linkme)]
283            static ENTRY: fn() -> $crate::late_macro_internals::LateStructEntry =
284                $crate::late_macro_internals::LateStructEntry::of::<$ty>;
285        };
286    )*};
287}
288
289/// Implements the [`LateField`] trait for the specified `$ty` type, turning it into a marker type
290/// that can be used to refer to a field within a late-initialized structure.
291///
292/// See the [crate level documentation](crate) for examples of this macro in action as well as a
293/// step-by-step guide on how to use it.
294///
295/// The `$ns` type specifies a [`LateStruct`] (defined by an earlier
296/// [`late_struct!`](crate::late_struct) invocation) into which this field will be placed. The
297/// optional `$val` type specifies the type of the value this field stores. If `$val` is omitted, it
298/// will default to `$ty`.
299///
300/// `$val` (or `$ty`, if `$val` is omitted) is expected to live for `'static`, be [`Sized`], and
301/// implement [`Default`]. It is this `Default` trait implementation which is used to initialize the
302/// fields of a [`LateInstance`](super::LateInstance). In addition, the field value must be able to
303/// coerce into the `$ns` structure type's [`LateStruct::EraseTo`] associated type. By default, the
304/// `LateStruct::EraseTo` associated type is set to `dyn 'static + fmt::Debug` and thus `$val` will
305/// be expected to implement [`Debug`](std::fmt::Debug) as well.
306///
307/// If `$val` is distinct from `$ty`, the `$ty` type need only be [`Sized`] and live for `'static`.
308///
309/// No types in this macro invocation may involve unbound generic parameters. You can specify more
310/// than one type onto which `LateField` should be implemented with this macro.
311#[macro_export]
312macro_rules! late_field {
313    (
314        $($ty:ty [$ns:ty] $(=> $val:ty)?),*
315        $(,)?
316    ) => {$(
317        const _: () = {
318            static DESCRIPTOR: $crate::late_macro_internals::RawLateFieldDescriptor =
319                $crate::late_macro_internals::new_late_field_descriptor::<$ns, $ty>();
320
321            impl $crate::late_macro_internals::LateFieldSealed<$ns> for $ty {}
322
323            unsafe impl $crate::late_macro_internals::LateField<$ns> for $ty {
324                type Value = $crate::late_macro_internals::OrDefault<$ty $(, $val)?>;
325
326                fn raw_descriptor() -> &'static $crate::late_macro_internals::RawLateFieldDescriptor {
327                    &DESCRIPTOR
328                }
329
330                fn coerce(value: *mut Self::Value) -> *mut <$ns as $crate::late_macro_internals::LateStruct>::EraseTo {
331                    value
332                }
333            }
334
335            #[cfg(target_family = "wasm")]
336            $crate::late_macro_internals::inventory::submit! {
337                $crate::late_macro_internals::LateFieldEntryWrapper(
338                    $crate::late_macro_internals::LateFieldEntry::of::<$ns, $ty>,
339                )
340            }
341
342            #[cfg(not(target_family = "wasm"))]
343            #[$crate::late_macro_internals::linkme::distributed_slice(
344                $crate::late_macro_internals::LATE_FIELDS
345            )]
346            #[linkme(crate = $crate::late_macro_internals::linkme)]
347            static ENTRY: fn() -> $crate::late_macro_internals::LateFieldEntry =
348                $crate::late_macro_internals::LateFieldEntry::of::<$ns, $ty>;
349        };
350    )*};
351}