wiwi/builder.rs
1//! Compile time checked builder APIs
2
3// # (temporary) Checklist for manually writing builders
4//
5// - struct definition
6// - impl struct with `builder()` and `finish_init(..)`
7// - submodule for builder impl details
8// - imports
9// - pub type for init/uninit
10// - builder struct def
11// - builder state trait def
12// - builder state container struct def
13// - private mod for sealed trait
14// - impl builder state trait
15// - impl sealed
16// - impl uninit for `new()` fn
17// - impl<S> where S: builder state trait for all the fns including `build()`
18// (`build()` calls `finish_init(..)`)
19// - impl block, same as previous one in headers and stuffs, for the internal fns
20
21use crate::prelude::*;
22
23pub struct Uninit {
24 __private: ()
25}
26
27pub struct Init<T = ()>
28where
29 T: ?Sized
30{
31 __marker: PhantomDataInvariant<T>
32}
33
34/// Trait for marker structs to hold state about if a field in
35/// a builder is initialised or not
36///
37/// # Safety
38///
39/// [`IS_INIT`] and [`IS_UNINIT`] must both be set correctly to acccurately
40/// represent the state of the field. If you set [`IS_INIT`] correctly, there is
41/// a default implementation for [`IS_UNINIT`] which is just an inversion of
42/// [`IS_INIT`], and therefore always correct.
43///
44/// [`IS_INIT`]: InitStatus::IS_INIT
45/// [`IS_UNINIT`]: InitStatus::IS_UNINIT
46pub unsafe trait InitStatus {
47 const IS_INIT: bool;
48 const IS_UNINIT: bool = !Self::IS_INIT;
49}
50
51// SAFETY: `Uninit` represents uninitialised
52unsafe impl InitStatus for Uninit {
53 const IS_INIT: bool = false;
54}
55
56// SAFETY: `Init` represents initialised
57unsafe impl<T> InitStatus for Init<T>
58where
59 T: ?Sized
60{
61 const IS_INIT: bool = true;
62}
63
64/// Marker trait for marker structs that represent uninitialised state
65///
66/// # Safety
67///
68/// Marker struct must actually represent an uninitialised state.
69// ?????
70// #[diagnostic::on_unimplemented(
71// message = "this field has already been initialised"
72// )]
73pub unsafe trait IsUninit: InitStatus {}
74
75// SAFETY: `Uninit` represents uninitialised
76unsafe impl IsUninit for Uninit {}
77
78/// Marker trait for marker structs that represent initialised state
79///
80/// # Safety
81///
82/// Marker struct must actually represent an initialised state.
83pub unsafe trait IsInit: InitStatus {}
84
85// SAFETY: `Init` represents initialised
86unsafe impl<T> IsInit for Init<T>
87where
88 T: ?Sized
89{}
90
91// todo i dont know if this is useful as a generic thing
92// /// Types that can be used safely for slots of type `T`
93// ///
94// /// For example, if we have an API that is expecting a string, we would
95// /// implement this trait for string types.
96// pub unsafe trait Slot<S>
97// where
98// Self: Sized
99// {
100// type TypeMarker: TypeMarker;
101//
102// /// Output type of reading from a slot previously written to (can be anything)
103// type Result: Sized;
104//
105// /// Writes `self` into `slot`, doing as little work as needed
106// ///
107// /// Work should be deferred to [`read`], if possible.
108// ///
109// /// # Safety
110// ///
111// /// Implementors must store a value into `slot`, that [`read`]
112// /// can read back later. [`read`] is allowed to rely on the fact that
113// /// something has been written.
114// ///
115// /// It may be undesireable to call [`write`] twice on the same `slot`,
116// /// but even with that, there is no _strict_ requirement that callers only
117// /// call [`write`] once.
118// ///
119// /// [`read`]: Slot::read
120// /// [`write`]: Slot::write
121// unsafe fn write(self, slot: &mut S);
122//
123// /// Reads back what was written to `slot` in [`write`], then processes it
124// /// into the read output type as necessary
125// ///
126// /// # Safety
127// ///
128// /// Implementors can assume that [`write`] has been called on `slot` and a
129// /// value has been written as expected.
130// ///
131// /// Callers must call [`write`] on this slot first, before passing it
132// /// to this function.
133// ///
134// /// [`write`]: Slot::write
135// unsafe fn read(slot: S) -> Self::Result;
136//
137// /// Converts the read output type into a reference of type
138// /// [`&ExternAny`](crate::ExternAny)
139// ///
140// /// This function is used so implementors can return non reference types.
141// /// Trait consumers would then take ownership of the provided output, and
142// /// use this function to get a reference type [`&ExternAny`](crate::ExternAny)
143// /// from it.
144// fn as_ref(result: &Self::Result) -> &crate::ExternAny;
145// }
146
147#[macro_export]
148macro_rules! gen_builder_state {
149 {
150 $(
151 $(#[$state_meta:meta])*
152 state
153 )?
154
155 $(
156 $(#[$container_meta:meta])*
157 container
158 )?
159
160 $(
161 $(#[$uninit_meta:meta])*
162 uninit
163 )?
164
165 $(
166 field $field:ident;
167 init $field_init:ident;
168 )*
169 } => {
170 $($(#[$state_meta])*)?
171 pub trait State {
172 $(
173 type $field: InitStatus;
174 type $field_init<T: ?Sized, M: ?Sized + TypeMarker>: State;
175 )*
176 }
177
178 #[allow(
179 unused_parens,
180 reason = "automatically generated"
181 )]
182 #[expect(
183 clippy::allow_attributes,
184 reason = "automatically generated (lint might not actually trigger, depending on input)"
185 )]
186 $($(#[$container_meta])*)?
187 pub struct StateContainer<$($field),*> {
188 __marker: PhantomDataInvariant<(
189 $($field),*
190 )>
191 }
192
193 gen_state! {
194 @impl gen_uninit
195 $(
196 $(#[$uninit_meta])*
197 uninit
198 )?
199
200 {}
201 { $($field)* }
202 }
203
204 impl<$(
205 $field: InitStatus
206 ),*> State for StateContainer<$($field),*> {
207 gen_state! {
208 @impl state_init_types
209 {}
210 {}
211 { $($field $field_init)* }
212 }
213 }
214 };
215
216 {
217 @impl gen_uninit
218 $(
219 $(#[$uninit_meta:meta])*
220 uninit
221 )?
222
223 { $($uninit_type:ident)* }
224 {
225 $field:ident
226 $($field_rest:ident)*
227 }
228 } => {
229 gen_state! {
230 @impl gen_uninit
231 $(
232 $(#[$uninit_meta])*
233 uninit
234 )?
235
236 {
237 $($uninit_type)*
238 Uninit
239 }
240 { $($field_rest)* }
241 }
242 };
243
244 {
245 @impl gen_uninit
246 $(
247 $(#[$uninit_meta:meta])*
248 uninit
249 )?
250
251 { $($uninit_type:ident)* }
252 {}
253 } => {
254 pub type StateUninit = StateContainer<
255 $($uninit_type),*
256 >;
257 };
258
259 {
260 @impl state_init_types
261 {}
262 {}
263 {}
264 } => {};
265
266 {
267 @impl state_init_types
268 {}
269 {}
270 {
271 $field_next:ident $field_init_next:ident
272 $($field_rest:ident $field_init_rest:ident)*
273 }
274 } => {
275 gen_state! {
276 @impl state_init_types
277 {}
278 { $field_next $field_init_next }
279 { $($field_rest $field_init_rest)* }
280 }
281 };
282
283 {
284 @impl state_init_types
285 { $($field_prev:ident $field_init_prev:ident)* }
286 { $field:ident $field_init:ident }
287 {
288 $field_next:ident $field_init_next:ident
289 $($field_rest:ident $field_init_rest:ident)*
290 }
291 } => {
292 type $field = $field;
293 type $field_init<T: ?Sized, M: ?Sized + TypeMarker> = StateContainer<
294 $($field_prev,)*
295 Init<T, M>,
296 $field_next,
297 $($field_rest),*
298 >;
299
300 gen_state! {
301 @impl state_init_types
302 {
303 $($field_prev $field_init_prev)*
304 $field $field_init
305 }
306 { $field_next $field_init_next }
307 { $($field_rest $field_init_rest)* }
308 }
309 };
310
311 {
312 @impl state_init_types
313 { $($field_prev:ident $field_init_prev:ident)* }
314 { $field:ident $field_init:ident }
315 {}
316 } => {
317 type $field = $field;
318 type $field_init<T: ?Sized, M: ?Sized + TypeMarker> = StateContainer<
319 $($field_prev,)*
320 Init<T, M>
321 >;
322 };
323}
324pub use gen_builder_state;
325
326#[macro_export]
327macro_rules! gen_builder_state_2 {
328 {
329 $(
330 ident $field:ident;
331
332 $(
333 $(#[$field_meta:meta])*
334 field;
335
336 $(#[$init_meta:meta])*
337 init;
338
339 $(#[$init_with_meta:meta])*
340 init_with;
341 )?
342 )*
343 } => {
344 $crate::__internal_proc_macros::__builder_internal_helper_gen_state! {
345 // worst macro syntax ever
346 // but it works lol?
347 $(
348 $($(__field_meta { #[$field_meta] })*)?
349 $($(__init_meta { #[$init_meta] })*)?
350 $($(__init_with_meta { #[$init_with_meta] })*)?
351 $field
352 )*
353 }
354 }
355}
356pub use gen_builder_state_2;
357
358pub type PhantomDataInvariant<T> = PhantomData<fn(T) -> T>;