1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Contains the safe builder type core.
//!
//! Field setters, drop logic, and optional trait impls are defined via
//! [`super::fields`] and [`super::drop`].
use proc_macro2::TokenStream;
use super::{BUILDER_BUILD_MUST_USE, BUILDER_MUST_USE, EmitContext};
use crate::model::*;
use crate::util::*;
pub fn emit_main(ctx: &EmitContext<'_>) -> TokenStream {
let EmitContext {
target,
builder,
builder_vis,
unchecked_builder,
unchecked_builder_vis,
impl_generics,
ty_generics,
struct_generics,
where_clause,
fields,
..
} = ctx;
let t_true = simple_ident("true");
let builder_doc = format!("A builder type for [`{target}`].");
let field_generics1 = fields.gen_names();
let field_generics2 = fields.gen_names();
let field_generics3 = fields.gen_names();
// exclude non-defaulted fields in `build` generic params
// and require them to always be `true`
let build_gens = fields
.pub_api()
.map(|f| f.default.is_some().then_some(&f.gen_name));
let build_params = build_gens.clone().flatten();
let build_args = build_gens.map(|f| f.unwrap_or(&t_true));
quote::quote! {
#[doc = #builder_doc]
#[repr(transparent)]
#[must_use = #BUILDER_MUST_USE]
#builder_vis struct #builder < #struct_generics #( const #field_generics1: ::core::primitive::bool = false ),* > #where_clause {
/// Inner unchecked builder. To move this value out, use [`Self::into_unchecked`].
/// Honestly, don't use this directly.
///
/// # Safety
///
/// The fields specified by the const generics on [`Self`] have to be initialized in `inner`.
inner: #unchecked_builder < #ty_generics >,
/// Note that `inner` has a safety invariant.
_unsafe: (),
}
impl < #impl_generics > #builder < #ty_generics > #where_clause {
/// Creates a new builder.
#[inline]
pub const fn new() -> Self {
// SAFETY: `new` initializes optional fields and no other
// field is considered initialized by the const generics
unsafe { #unchecked_builder::new().assert_init() }
}
}
impl < #impl_generics #( const #field_generics2: ::core::primitive::bool ),* > #builder < #ty_generics #(#field_generics3),* > #where_clause {
/// Unwraps this builder into its unsafe counterpart.
///
/// This isn't unsafe in itself, however using it carelessly may lead to
/// leaking objects and not dropping initialized values.
#[inline]
#unchecked_builder_vis const fn into_unchecked(self) -> #unchecked_builder < #ty_generics > {
// the way this function is written tries to reduce the amount of runtime code
// generated for unoptimized/debug builds without impacting optimized code.
// this is morally equivalent to `ptr::read(&ManuallyDrop::new(self).inner)`, but
// that isn't usably in const as of now. this is only needed to deconstruct `self`
// because it has a `Drop` impl.
// put `self` into `ManuallyDrop` so its destructor doesn't run
let this = ::core::mem::ManuallyDrop::new(self);
// `ManuallyDrop` is transparent over the inner type, so cast back
let this = &raw const this as *const Self;
// SAFETY: `self` won't be dropped so we can move out `inner` safely
unsafe { ::core::ptr::read(&raw const (*this).inner) }
}
}
impl < #impl_generics #( const #build_params: ::core::primitive::bool ),* > #builder < #ty_generics #(#build_args),* > #where_clause {
/// Returns the finished value.
///
/// This function can only be called when all required fields have been set.
#[must_use = #BUILDER_BUILD_MUST_USE]
#[inline]
pub const fn build(self) -> #target < #ty_generics > {
unsafe {
// SAFETY: generics assert that all required fields were initialized
// optional fields were set by `Self::new`.
self.into_unchecked().build()
}
}
}
}
}
pub fn emit_builder_fn(ctx: &EmitContext<'_>) -> TokenStream {
let EmitContext {
target,
builder,
builder_vis,
builder_fn,
impl_generics,
ty_generics,
where_clause,
..
} = ctx;
let Some(builder_fn) = builder_fn else {
return TokenStream::new();
};
quote::quote! {
impl < #impl_generics > #target < #ty_generics > #where_clause {
/// Creates a new builder for this type.
#[inline]
#builder_vis const fn #builder_fn() -> #builder < #ty_generics > {
#builder::new()
}
}
}
}