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>;