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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
use derive_deftly::define_derive_deftly;
define_derive_deftly! {
pub Builder expect items =
// Define common identifiers. Here, 'B' is short for "builder".
// ----- Extract parameters about the builder's name.
// The name of the FooBuilder itself.
${define Bname ${ if tmeta(builder(name)) { ${tmeta(builder(name))} } else { $<$tname Builder> }}}
// The name of the FooBuilderError type.
${define Berror ${
if tmeta(builder(build_fn(error))) {
${tmeta(builder(build_fn(error)))}
} else if tmeta(builder(name)) {
$< ${tmeta(builder(name))} Error >
} else {
$<$tname BuilderError>
}}}
// If true, we are using a user-provided error type.
${defcond B_CUSTOM_ERROR tmeta(builder(build_fn(error))) }
// The _type_ of the FooBuilder, with all its generic parameters.
${define Btype { $Bname <$tgens> }}
// ----- Extract parameters related to setter names.
// The specific name to use for each field's setter function.
${define F_SETTER_NAME ${
if fmeta(builder(setter(name))) { ${fmeta(builder(setter(name)))} }
else if fmeta(builder(setter(prefix))) { $< ${fmeta(builder(setter(prefix)))} _ $fname > }
else if tmeta(builder(setter(prefix))) { $< ${tmeta(builder(setter(prefix)))} _ $fname > }
else { $fname }
}}
// ------ Extract "pattern" parameters on type and field.
// (These define how setters and builders consume "self".)
// The "pattern=" option at the type level, defaulting to "mutable"
${define T_OPT_PATTERN ${if tmeta(builder(pattern)) { ${tmeta(builder(pattern)) as tokens} } else { mutable } }}
// The "pattern=" optoin at the field level, defaulting to the type-level option.
${define F_OPT_PATTERN ${if fmeta(builder(pattern)) { ${fmeta(builder(pattern)) as tokens} } else { $T_OPT_PATTERN } }}
// Conditions: see which global pattern is set. These affect the build() function.
${defcond T_PAT_OWN approx_equal($T_OPT_PATTERN, owned)}
${defcond T_PAT_IMM approx_equal($T_OPT_PATTERN, immutable)}
${defcond T_PAT_MUT approx_equal($T_OPT_PATTERN, mutable)}
// Conditions: see which field pattern is set. These affect the setter functions.
${defcond F_PAT_OWN approx_equal($F_OPT_PATTERN, owned)}
${defcond F_PAT_IMM approx_equal($F_OPT_PATTERN, immutable)}
${defcond F_PAT_MUT approx_equal($F_OPT_PATTERN, mutable)}
// Enforce that at most one of T_PAT_OWN,T_PAT_IMM,T_PAT_MUT was declared.
${select1 T_PAT_OWN {} else if T_PAT_IMM {} else if T_PAT_MUT {}}
// ------ Extract "setter(skip)" and "setter(custom)" and "setter (enabled)" on type and field.
${defcond T_OPT_SETTER_SKIP tmeta(builder(setter(skip)))}
${defcond T_OPT_SETTER_CUST tmeta(builder(setter(custom)))}
${defcond T_OPT_SETTER_DFLT tmeta(builder(setter(enabled)))}
${defcond F_OPT_SETTER_SKIP fmeta(builder(setter(skip)))}
${defcond F_OPT_SETTER_CUST fmeta(builder(setter(custom)))}
${defcond F_OPT_SETTER_DFLT fmeta(builder(setter(enabled)))}
// -- Derived conditions to say whether our fields are have setters or not.
// Are we using the default setter behavior at the type level?
${defcond T_SETTER_DFLT any(T_OPT_SETTER_DFLT, not(any(T_OPT_SETTER_SKIP, T_OPT_SETTER_CUST)))}
// Per-field: Have we set any explicit field setter behavior?
${defcond F_SETTER_EXPLICIT any(F_OPT_SETTER_SKIP, F_OPT_SETTER_CUST, F_OPT_SETTER_DFLT)}
// Per-field: Are we using setter(skip)?
${defcond F_SETTER_SKIP any(F_OPT_SETTER_SKIP, all(T_OPT_SETTER_SKIP, not(F_SETTER_EXPLICIT)))}
// Per-field: Are we using setter(custom)?
${defcond F_SETTER_CUST any(F_OPT_SETTER_CUST, all(T_OPT_SETTER_CUST, not(F_SETTER_EXPLICIT)))}
// Per-field: Are we using setter(enabled)?
${defcond F_SETTER_DFLT any(F_OPT_SETTER_DFLT, all(T_SETTER_DFLT, not(F_SETTER_EXPLICIT)))}
// Enforce that we exactly one of these flags set.
${select1 T_OPT_SETTER_SKIP {} else if T_OPT_SETTER_CUST {} else if T_SETTER_DFLT {}}
// ------ Extract parameters to tell us how to handle setter arguments.
${defcond F_SETTER_INTO fmeta(builder(setter(into))) }
${defcond F_SETTER_TRY_INTO fmeta(builder(setter(try_into))) }
${defcond F_SETTER_STRIP_OPTION fmeta(builder(setter(strip_option))) }
// ------ Extract visibility-related parameters.
// Visibility for top-level items.
${define T_VIS ${if tmeta(builder(vis)) { ${tmeta(builder(vis))} } else { $tvis }}}
// Per-field: visibility for individual setters.
${define F_VIS ${if fmeta(builder(vis)) { ${fmeta(builder(vis))} } else { $T_VIS }}}
// ------ Extract defaulting-related parameters.
${defcond F_OPT_DEFAULT fmeta(builder(default)) }
${defcond F_OPT_DEFAULT_VAL fmeta(builder(default_val)) }
// ------ Extract field-type parameters.
${defcond F_OPT_FIELD_TY fmeta(builder(field(ty)))}
// TODO: This type is a bit convoluted in its actual use.
${define F_TY ${if F_OPT_FIELD_TY {
${fmeta(builder(field(ty)))}
} else {
$ftype
}}}
${defcond F_OPT_FIELD_BUILD_FN fmeta(builder(field(build_fn)))}
${defcond F_OPT_FIELD_TRY_BUILD_FN fmeta(builder(field(try_build_fn)))}
// ------ Extract sub-builder options.
${defcond F_OPT_SUB_BUILDER fmeta(builder(sub_builder))}
${define F_OPT_SUB_BUILDER_FN_NAME ${
if fmeta(builder(sub_builder(fn_name))) {
${fmeta(builder(sub_builder(fn_name)))}
} else if F_OPT_SUB_BUILDER{
build
}}}
// (This is a bit kludgey; see derive-deftly#58)
${defcond T_ANY_SUB_BUILDER not(approx_equal(
{ ${for fields { ${if F_OPT_SUB_BUILDER {yes}}}} },
{}
))}
${define F_SUB_BUILDER_TY ${if F_OPT_FIELD_TY { $F_TY } else { $<$ftype Builder> } }}
// ------ Extract parameters about the build function
// Do we skip making the build function entirely?
${defcond BLD_OPT_SKIP tmeta(builder(build_fn(skip)))}
// What name do we use for the build function we generate?
${define BLD_OPT_FN_NAME ${if tmeta(builder(build_fn(name))) { ${tmeta(builder(build_fn(name)))} } else { build }}}
// What code do we insert into the build function to implement validation?
${define BLD_OPT_VALIDATE ${
if tmeta(builder(build_fn(validate))) {
let _ignore =
${tmeta(builder(build_fn(validate))) as tokens}(self)
.map_err($Berror::from)?;
} else {
}
}}
// ------
// Define aliases for std types that are usually in the prelude.
${define Result {::std::result::Result}}
${define String {::std::string::String}}
${define Default {::std::default::Default}}
${define IMPORTS {
#[allow(unused)]
use ::std::{clone::Clone as _,
convert::{Into as _, TryInto as _}};
}}
// ------
// Helper to copy certain attributes to fields and setters.
${define F_COPIED_ATTRS {
${fattrs doc, cfg, allow}
}}
//=============================================================
// IMPLEMENT FUNCTIONS IN THE ORIGINAL TYPE.
// Here we add a `builder()` function to the original type,
// returning a builder for this type.
impl<$tgens> $ttype<$tgens> where $twheres {
#[doc = "Return a new builder to construct an instance of this type."]
#[allow(dead_code)]
$T_VIS fn builder() -> $Btype {
$Bname::default()
}
}
//=============================================================
// DECLARE THE FooBuilder STRUCTURE.
// Document it.
#[doc = "A builder type, used to construct a `"]
#[doc = stringify!($tname)]
#[doc = "`"]
// Every builder has to derive(Default).
#[derive(Default)]
// Any builder using the `mutable` or `immutable` pattern has to derive(Clone).
${if not(T_PAT_OWN) {
#[derive(Clone)]
}}
// Derive any other traits that we were told to derive.
${if tmeta(builder(derive)) {
#[derive( ${tmeta(builder(derive)) as tokens} )]
}}
// Apply any explicit attributes we were told to apply.
${if tmeta(builder(struct_attr)) {
${tmeta(builder(struct_attr)) as tokens}
}}
$T_VIS struct $Btype where $twheres {
$(
// For each field in the original type, we add a corresponding field to the
// builder.
// Some attributes are automatically copied from the original builder.
$F_COPIED_ATTRS
// Apply any explicit attributes we were told to apply.
${if fmeta(builder(field_attr)) {
${fmeta(builder(field_attr)) as tokens}
}}
${if F_OPT_FIELD_TY {
// If we were told the type to put into the builder, use that type.
$fname : $F_TY,
} else if F_OPT_SUB_BUILDER {
// If this is a sub-builder, us the sub-builder type.
$fname : $F_SUB_BUILDER_TY,
} else if F_SETTER_SKIP {
// If we're skipping the field, use a PhantomData.
$fname : ::std::marker::PhantomData<$ftype>,
} else {
// Otherwise, we default to using an Option<F>.
$fname : ::std::option::Option<$ftype>,
}}
)
}
//=============================================================
// IMPLEMENT FUNCTIONS ON THE FooBuilder
#[allow(dead_code)]
// Apply any explicit attributes we were told to apply.
${if tmeta(builder(impl_attr)) {
${tmeta(builder(impl_attr)) as tokens}
}}
impl<$tgens> $Btype where $twheres {
#[doc = "Create a new `"]
#[doc = stringify!($Bname)]
#[doc = "`."]
$T_VIS fn new() -> Self {
Self::default()
}
// ========================================================
// IMPLEMENT ONE SETTER FUNCTION FOR EACH FIELD.
$(
// Enforce that at most one pattern type is set.
${select1 F_PAT_OWN {} else if F_PAT_IMM {} else if F_PAT_MUT {}}
// Enforce at most one skip/custom flag.
${select1 F_SETTER_SKIP {} else if F_SETTER_CUST {} else if F_SETTER_DFLT {}}
// Enforce at most one of try_into/into.
${select1 F_SETTER_TRY_INTO {} else if F_SETTER_INTO {} else {} }
// We suppress the setter method when we aren't emitting a default setter.
${when F_SETTER_DFLT}
// Type that the setter's "self" argument should have.
${define SETTER_ARG_SELF
${select1
F_PAT_MUT {&mut self}
else if F_PAT_IMM {&self}
else if F_PAT_OWN {self}
}}
// Type that we intend to accept, possibly having stripped an Option.
${define SETTER_ARGTYPE_INNER
${if F_SETTER_STRIP_OPTION { $crate::strip_option!{ $F_TY } }
else { $F_TY }
}}
// Generics for the setter function.
${define SETTER_GENS
${if F_SETTER_INTO { < V: ::std::convert::Into<$SETTER_ARGTYPE_INNER> > }
else if F_SETTER_TRY_INTO { <V: ::std::convert::TryInto<$SETTER_ARGTYPE_INNER>> }
else {}
}
}
// Type of the setter's value argument.
${define SETTER_ARGTYPE
${if any(F_SETTER_INTO,F_SETTER_TRY_INTO) { V }
else { $SETTER_ARGTYPE_INNER }
}
}
// Stub method invocation to call into() or try_into() on argument.
${define SETTER_CVT_ARG
${if F_SETTER_INTO { .into() } else if F_SETTER_TRY_INTO { .try_into()? }}
}
// Type of self that the setter should return.
${define SETTER_RET_SELF
${select1
F_PAT_MUT {&mut Self}
else if F_PAT_IMM {Self}
else if F_PAT_OWN {Self}
}}
// Type that the setter should return.
${define SETTER_RET
${if F_SETTER_TRY_INTO {
$Result<$SETTER_RET_SELF, V::Error>
} else {
$SETTER_RET_SELF
}}}
// Statement to set `this_` based on `self.
${define SETTER_INIT_THIS
${select1
F_PAT_MUT {let this_ = self;}
else if F_PAT_IMM {let mut this_ = self.clone();}
else if F_PAT_OWN {let mut this_ = self;}
}}
${if F_OPT_SUB_BUILDER {
// This is the sub_builder case.
#[doc = "Return a builder for the `"]
#[doc = stringify!($fname)]
#[doc = "` field."]
// Some attributes are automatically copied from the original builder.
$F_COPIED_ATTRS
// Apply any explicit attributes we were told to apply.
${if fmeta(builder(setter_attr)) {
${fmeta(builder(setter_attr)) as tokens}
}}
// For a sub_builder, we just return a mutable reference to the
// sub-builder.
$F_VIS fn $F_SETTER_NAME (&mut self) -> &mut $F_SUB_BUILDER_TY {
&mut self.$fname
}
} else {
// This is the case where sub_builder is _not_ provided.
#[doc = "Set the `"]
#[doc = stringify!($fname)]
#[doc = "` field on this builder."]
// Some attributes are automatically copied from the original builder.
$F_COPIED_ATTRS
// Apply any explicit attributes we were told to apply.
${if fmeta(builder(setter_attr)) {
${fmeta(builder(setter_attr)) as tokens}
}}
$F_VIS fn $F_SETTER_NAME $SETTER_GENS ($SETTER_ARG_SELF, value: $SETTER_ARGTYPE) -> $SETTER_RET {
$IMPORTS
// Construct this_ as a copy of, or reference to, self.
$SETTER_INIT_THIS
// Set this.$fname. Depending on the options, we will need to
// add one or more Some(.)s.
//
// In every case, we use $SETTER_CVT_ARG to call into() or
// try_into() as appropriate.
this_.$fname = ${
// TODO: This $if could be terser!
if all(F_OPT_FIELD_TY, F_SETTER_STRIP_OPTION) {
Some(value $SETTER_CVT_ARG)
} else if F_OPT_FIELD_TY {
value $SETTER_CVT_ARG
} else if F_SETTER_STRIP_OPTION {
Some(Some(value $SETTER_CVT_ARG))
} else {
Some(value $SETTER_CVT_ARG)
}};
// Return this_ for chaining. If we want to return a Result,
// we wrap it in Ok().
${if F_SETTER_TRY_INTO {
Ok(this_)
} else {
this_
}}
}
}}
)
// ========================================================
// IMPLEMENT THE BUILDER FUNCTION.
// Type that the builder function should take.
${define BLD_ARG
${if any(T_PAT_MUT, T_PAT_IMM) {&self} else {self}} }
// If the builder needs to clone itself, `.clone()`; otherwise empty.
${define BLD_MAYBE_CLONE
${if any(T_PAT_MUT, T_PAT_IMM) { .clone() } else {}} }
// Code fragment to convert an Option<T> into T, and handle the case
// where a given option wasn't set.
${define BLD_HANDLE_NONE ${
if F_OPT_DEFAULT {
.unwrap_or_else($Default::default)
} else if F_OPT_DEFAULT_VAL {
.unwrap_or_else(|| ${fmeta(builder(default_val)) as tokens})
} else {
.ok_or_else(|| $crate::UninitializedFieldError::new(stringify!($fname)))?
}
}}
${if not(BLD_OPT_SKIP) {
#[doc="Build a new `"]
#[doc=stringify!($tname)]
#[doc="`. Gives an error if any required field is missing or invalid."]
$T_VIS fn $BLD_OPT_FN_NAME($BLD_ARG) -> $Result<$ttype, $Berror> {
$IMPORTS
// First, call the validate function (if we have one.)
$BLD_OPT_VALIDATE
// Construct the object...
Ok($tname {
$(
// For each field...
${if F_OPT_FIELD_TRY_BUILD_FN {
// If we got an explicit try_build_fn, invoke it on self
// and propagate errors.
$fname: (${fmeta(builder(field(try_build_fn)))})(self)?,
} else if F_OPT_FIELD_BUILD_FN {
// If we got an explicit build_fn, invoke it on self.
$fname: (${fmeta(builder(field(build_fn)))})(self),
} else if F_OPT_SUB_BUILDER {
// If we're using a sub-builder, call its build function.
$fname: self.$fname.$F_OPT_SUB_BUILDER_FN_NAME()
.map_err(|e| $crate::SubfieldBuildError::new(stringify!($fname),e))?
} else if F_SETTER_SKIP {
// If we skipped the setter (and field), call default.
$fname: $Default::default(),
} else {
// This is the regular case: we clone (if we aren't
// consuming), and we handle the case where the option
// wasn't set.
$fname: self.$fname
$BLD_MAYBE_CLONE
$BLD_HANDLE_NONE,
}}
// Make sure we only got one of default and default_val.
${select1 F_OPT_DEFAULT {} else if F_OPT_DEFAULT_VAL {} else {} }
// Check for other incompatible options
${select1 F_OPT_FIELD_TRY_BUILD_FN {} else if F_OPT_FIELD_BUILD_FN {} else if F_OPT_SUB_BUILDER {} else {}}
)
})
}
}}
}
//=============================================================
// CREATE THE FooBuilderError type.
${ if not(B_CUSTOM_ERROR) {
#[derive(Clone, Debug)]
#[non_exhaustive]
$T_VIS enum $Berror {
UninitializedField(&'static str),
ValidationError($String),
${if T_ANY_SUB_BUILDER {
SubfieldBuildError(&'static str, String),
}}
}
impl From<$crate::UninitializedFieldError> for $Berror {
fn from(e: $crate::UninitializedFieldError) -> Self {
Self::UninitializedField(e.field_name())
}
}
${if T_ANY_SUB_BUILDER {
impl<E: ::std::string::ToString> From<$crate::SubfieldBuildError<E>> for $Berror {
fn from(e: $crate::SubfieldBuildError<E>) -> Self {
Self::SubfieldBuildError(e.field_name(), e.sub_builder_error().to_string())
}
}
}}
impl From<$String> for $Berror {
fn from(s: $String) -> Self {
Self::ValidationError(s)
}
}
impl ::std::fmt::Display for $Berror {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> $Result<(), ::std::fmt::Error> {
match self {
$Berror::UninitializedField(field) =>
write!(f, "Uninitialized field: {field}")?,
$Berror::ValidationError(msg) =>
write!(f, "Unable to build: {msg}")?,
${if T_ANY_SUB_BUILDER {
$Berror::SubfieldBuildError(fld, msg) =>
write!(f, "Unable to build {fld}: {msg}")?,
}}
}
Ok(())
}
}
impl ::std::error::Error for $Berror {}
}}
}
pub use derive_deftly_template_Builder;