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
//! Common macro elements for deriving parsers and encoders
use derive_deftly::{define_derive_deftly, define_derive_deftly_module};
define_derive_deftly! {
/// Defines a constructor struct and method
//
// TODO maybe move this out of tor-netdoc, to a lower-level dependency
///
/// "Constructor" is a more lightweight alternative to the builder pattern.
///
/// # Comparison to builders
///
/// * Suitable for transparent, rather than opaque, structs.
/// * Missing fields during construction are detected at compile-time.
/// * Construction is infallible at runtime.
/// * Making a previously-required field optional is an API break.
///
/// # Input
///
/// * `struct Thing`. (enums and unions are not supported.)
///
/// * Each field must impl `Default` or be annotated `#[deftly(constructor)]`
///
/// * `Thing` should contain `#[doc(hidden)] __non_exhaustive: ()`
/// rather than being `#[non_exhaustive]`.
/// (Because struct literal syntax is not available otherwise.)
///
/// # Generated items
///
/// * **`pub struct ThingConstructor`**:
/// contains all the required (non-optional) fields from `Thing`.
/// `ThingConstructor` is `exhaustive`.
///
/// * **`fn ThingConstructor::construct(self) -> Thing`**:
/// fills in all the default values.
///
/// * `impl From<ThingConstructor> for Thing`
///
/// # Attributes
///
/// ## Field attributes
///
/// * **`#[deftly(constructor)]`**:
/// Include this field in `ThingConstructor`.
/// The caller must provide a value.
///
/// * **`#[deftly(constructor(default = "EXPR"))]`**:
/// Instead of `Default::default()`, the default value is EXPR.
/// EXPR cannot refer to anything in `ThingConstructor`.
// If we want that we would need to invent a feature for it.
///
/// # Example
///
/// ```
/// use derive_deftly::Deftly;
/// use tor_netdoc::derive_deftly_template_Constructor;
///
/// #[derive(Deftly, PartialEq, Debug)]
/// #[derive_deftly(Constructor)]
/// #[allow(clippy::manual_non_exhaustive)]
/// pub struct Thing {
/// /// Required field
/// #[deftly(constructor)]
/// pub required: i32,
///
/// /// Optional field
/// pub optional: Option<i32>,
///
/// /// Optional field with fixed default
/// #[deftly(constructor(default = "7"))]
/// pub defaulted: i32,
///
/// #[doc(hidden)]
/// __non_exhaustive: (),
/// }
///
/// let thing = Thing {
/// optional: Some(23),
/// ..ThingConstructor {
/// required: 12,
/// }.construct()
/// };
///
/// assert_eq!(
/// thing,
/// Thing {
/// required: 12,
/// optional: Some(23),
/// defaulted: 7,
/// __non_exhaustive: (),
/// }
/// );
/// ```
///
/// # Note
export Constructor for struct, beta_deftly:
${define CONSTRUCTOR_NAME $<$tname Constructor>}
${define CONSTRUCTOR $<$ttype Constructor>}
${defcond F_DEFAULT_EXPR fmeta(constructor(default))}
${defcond F_DEFAULT_TRAIT not(fmeta(constructor))}
${defcond F_REQUIRED not(any(F_DEFAULT_EXPR, F_DEFAULT_TRAIT))}
#[doc = ${concat "Constructor (required fields) for " $tname}]
///
#[doc = ${concat "See [`" $tname "`]."}]
///
/// This constructor struct contains precisely the required fields.
#[doc = ${concat "You can make a `" $tname
"` out of it with [`.construct()`](" $CONSTRUCTOR_NAME "::construct),"}]
/// or the `From` impl,
/// and use the result as a basis for further modifications.
///
/// # Example
///
/// ```rust,ignore
#[doc = ${concat "let " ${snake_case $tname} " = " $tname "{"}]
#[doc = ${concat ${for fields {
${if any(fmeta(constructor(default)), not(fmeta(constructor))) {
" " $fname ": /* optional field value */,\n"
} else {
}}
}}}]
#[doc = ${concat " .." $CONSTRUCTOR_NAME " {"}]
#[doc = ${concat ${for fields {
${if not(any(fmeta(constructor(default)), not(fmeta(constructor)))) {
" " $fname ": /* required field value */,\n"
} else {
}}
}}}]
#[doc = ${concat " }.construct()"}]
#[doc = ${concat "};"}]
/// ```
#[allow(clippy::exhaustive_structs)]
$tvis struct $CONSTRUCTOR_NAME<$tdefgens> where $twheres { $(
${when F_REQUIRED}
${fattrs doc}
$fdefvis $fname: $ftype,
) }
impl<$tgens> $CONSTRUCTOR where $twheres {
#[doc = ${concat "Construct a minimal [`" $tname "`]"}]
///
#[doc = ${concat "In the returned " $tname ","}]
/// optional fields all get the default values.
$tvis fn construct(self) -> $ttype {
$tname { $(
$fname: ${select1
F_REQUIRED {
self.$fname
}
F_DEFAULT_TRAIT {
::std::default::Default::default()
}
F_DEFAULT_EXPR {
${fmeta(constructor(default)) as expr}
}
},
) }
}
}
impl<$tgens> From<$CONSTRUCTOR> for $ttype where $twheres {
fn from(constructor: $CONSTRUCTOR) -> $ttype {
constructor.construct()
}
}
}
/// Macro to help check that netdoc items in a derive input are in the right order
///
/// Used only by the `NetdocParseable` derive-deftly macro.
#[doc(hidden)]
#[macro_export]
macro_rules! netdoc_ordering_check {
{ } => { compile_error!("netdoc must have an intro item so cannot be empty"); };
// When we have K0 P0 K1 P1 ...
// * Check that P0 and P1 have a consistent ordr
// * Continue with K1 P1 ...
// So we check each consecutive pair of fields.
{ $k0:ident $f0:ident $k1:ident $f1:ident $($rest:tt)* } => {
$crate::netdoc_ordering_check! { <=? $k0 $k1 $f1 }
$crate::netdoc_ordering_check! { $k1 $f1 $($rest)* }
};
{ $k0:ident $f0:ident } => {}; // finished
// Individual ordering checks for K0 <=? K1
//
// We write out each of the allowed this-kind next-kind combinations:
{ <=? intro $any:ident $f1:ident } => {};
{ <=? normal normal $f1:ident } => {};
{ <=? normal subdoc $f1:ident } => {};
{ <=? subdoc subdoc $f1:ident } => {};
// Not in the allowed list, must be an error:
{ <=? $k0:ident $k1:ident $f1:ident } => {
compile_error!(concat!(
"in netdoc, ", stringify!($k1)," field ", stringify!($f1),
" may not come after ", stringify!($k0),
));
};
}
define_derive_deftly_module! {
/// Common definitions for any netdoc derives
NetdocDeriveAnyCommon beta_deftly:
// Emit an eprintln with deftly(netdoc(debug)), just so that we don't get surprises
// where someone leaves a (debug) in where it's not implemented, and we later implement it.
${define EMIT_DEBUG_PLACEHOLDER {
${if tmeta(netdoc(debug)) {
// This messing about with std::io::stderr() mirrors netdoc_parseable_derive_debug.
// (We could use eprintln! #[test] captures eprintln! but not io::stderr.)
writeln!(
std::io::stderr().lock(),
${concat "#[deftly(netdoc(debug))] applied to " $tname},
).expect("write to stderr failed");
}}
}}
${define DOC_DEBUG_PLACEHOLDER {
/// * **`#[deftly(netdoc(debug))]`**:
///
/// Currently implemented only as a placeholder
///
/// The generated implementation may in future generate copious debug output
/// to the program's stderr when it is run.
/// Do not enable in production!
}}
}
define_derive_deftly_module! {
/// Common definitions for derives of structs containing items
///
/// Used by `NetdocParseable`, `NetdocParseableFields`,
/// `NetdocEncodable` and `NetdocEncodableFields`.
///
/// Importing template must define these:
///
/// * **`F_INTRO`**, **`F_SUBDOC`**, **`F_SIGNATURE`**
/// conditions for the fundamental field kinds which aren't supported everywhere.
///
/// The `F_FLATTEN`, `F_SKIP`, `F_NORMAL` field type conditions are defined here.
///
/// Importer must also import `NetdocDeriveAnyCommon`.
//
// We have the call sites import the other modules, rather than using them here, because:
// - This avoids the human reader having to chase breadcrumbs
// to find out what a particular template is using.
// - The dependency graph is not a tree, so some things would be included twice
// and derive-deftly cannot deduplicate them.
NetdocSomeItemsDeriveCommon beta_deftly:
// Is this field `flatten`?
${defcond F_FLATTEN fmeta(netdoc(flatten))}
// Is this field `skip`?
${defcond F_SKIP fmeta(netdoc(skip))}
// Is this field normal (non-structural)?
${defcond F_NORMAL not(any(F_SIGNATURE, F_INTRO, F_FLATTEN, F_SUBDOC, F_SKIP))}
// Field keyword as `&str`
${define F_KEYWORD_STR { ${concat
${if any(F_FLATTEN, F_SUBDOC, F_SKIP) {
${if F_INTRO {
${error "#[deftly(netdoc(subdoc))] (flatten) and (skip) not supported for intro items"}
} else {
// Sub-documents and flattened fields have their keywords inside;
// if we ask for the field-based keyword name for one of those then that's a bug.
${error "internal error, subdoc or skip KeywordRef"}
}}
}}
${fmeta(netdoc(keyword)) as str,
default ${concat ${kebab_case $fname}}}
}}}
// Field keyword as `&str` for debugging and error reporting
${define F_KEYWORD_REPORT ${concat
${if any(F_FLATTEN, F_SUBDOC, F_SKIP) { $fname }
else { $F_KEYWORD_STR }}
}}
// Field keyword as `KeywordRef`
${define F_KEYWORD { (KeywordRef::new_const($F_KEYWORD_STR)) }}
}
define_derive_deftly_module! {
/// Common definitions for derives of whole network documents
///
/// Used by `NetdocParseable` and `NetdocEncodable`.
///
/// Importer must also import `NetdocSomeItemsDeriveCommon` and `NetdocDeriveAnyCommon`.
NetdocEntireDeriveCommon beta_deftly:
// Predicate for the toplevel
${defcond T_SIGNATURES false}
// Predicates for the field kinds
${defcond F_INTRO all(not(T_SIGNATURES), approx_equal($findex, 0))}
${defcond F_SUBDOC fmeta(netdoc(subdoc))}
${defcond F_SIGNATURE T_SIGNATURES} // signatures section documents have only signature fields
// compile-time check that fields are in the right order in the struct
${define FIELD_ORDERING_CHECK {
${if not(T_SIGNATURES) { // signatures structs have only signature fields
netdoc_ordering_check! {
$(
${when not(F_SKIP)}
${select1
F_INTRO { intro }
F_NORMAL { normal }
F_FLATTEN { normal }
F_SUBDOC { subdoc }
}
$fname
)
}
}}
}}
}
define_derive_deftly_module! {
/// Common definitions for derives of flattenable network document fields structs
///
/// Used by `NetdocParseableFields` and `NetdocEncodableFields`.
///
/// Importer must also import `NetdocSomeItemsDeriveCommon` and `NetdocDeriveAnyCommon`.
NetdocFieldsDeriveCommon beta_deftly:
// Predicates for the field kinds, used by NetdocSomeItemsDeriveCommon etc.
${defcond F_INTRO false}
${defcond F_SUBDOC false}
${defcond F_SIGNATURE false}
${define DOC_NETDOC_FIELDS_DERIVE_SUPPORTED {
/// * The input struct can contain only normal non-structural items
/// (so it's not a sub-document with an intro item).
/// * The only attributes supported are the field attributes
/// `#[deftly(netdoc(keyword = STR))]`
/// `#[deftly(netdoc(default))]`
/// `#[deftly(netdoc(single_arg))]`
/// `#[deftly(netdoc(with = "MODULE"))]`
/// `#[deftly(netdoc(flatten))]`
/// `#[deftly(netdoc(skip))]`
}}
}
define_derive_deftly_module! {
/// Common definitions for derives of network document item value structs
///
/// Used by `ItemValueParseable` and `ItemValueEncodable`.
///
/// Importer must also import `NetdocDeriveAnyCommon`.
NetdocItemDeriveCommon beta_deftly:
${defcond F_REST fmeta(netdoc(rest))}
${defcond F_OBJECT fmeta(netdoc(object))}
${defcond F_NORMAL not(any(F_REST, F_OBJECT))}
${defcond T_IS_SIGNATURE tmeta(netdoc(signature))}
}