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}