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
//! Machinery for defining multiple varieties of network status document.
//!
//! This module handles re-using the same source code to define
//! * Votes, and parts thereof
//! * Consensuses, and parts thereof
//! * Microdescriptor consensuses, and parts thereof
//!
//! We call these three kinds of document "variety".
//! So a variety is either "vote", or a consensus flavour.
//!
//! # Overwview
//!
//! Network status documents, including consensuses (of various flavours) and votes,
//! have a lot of similarity.
//! But they also have a lot of fiddly ad-hoc differences.
//!
//! To deal with this similarity, and to avoid repeating too much code,
//! while still handling the variation, we do as follows:
//!
//! * Types which are simply the same for all varieties,
//! are defined in shared modules like
//! `tor_netdoc::doc::netstatus` (whole network status documents) and
//! `tor_netdoc::doc::netstatus::rs` (router status entries).
// We would like to linkify ^ these but that involves decorating many things pub(crate)
// only in #[cfg(doc)] which is quite annoying. These are just examples, anyway.
//!
//! * Types which are completely different between varieties
//! are defined in ordinary variety-specific modules like
//! `tor_netdoc::doc::netstatus::rs::md`
//! (router status entries in microdescriptor consensus).
//!
//! * Types which are *similar* across varieties, but not identical,
//! are handled via a per-variety macro-and-multiple-inclusion scheme.
//! That scheme is implemented in this module.
//!
//! * Types which are *similar* across consensus *flavours*, but not defined for *votes*,
//! are handled via a per-*flavour* macro-and-multiple-inclusion scheme.
//!
//! # Each-variety/flavour macro and multiple inclusion scheme
//!
//! This module contains macros that will be used to define
//! similar-but-not-identical types
//! for each document variety ("vote", "md", and "plain").
//!
//! The definition of such type is in an `each_variety.rs` file,
//! which will be included once for each variety.
//!
//! Types which are only implemented for votes are defined in `each_flavor.rs`.
//! which will be included once for each *flavour* (so not for votes).
//!
//! Within `each_variety.rs` and `each_flavor.rs` macros are available
//! (provided by the machinery here in `ns_variety_definition_macros`)
//! which can be used to define individual fields, or code fragments,
//! which vary between varieties.
//!
//! Inclusion of the `each_variety.rs` and `each_flavor.rs` files,
//! as adapted for the particular variety,
//! is done by calling `ns_do_variety_VARIETY`
//! in the (handwritten) specifies-specific module.
//!
//! For example, the call to `ns_do_variety_md!()`
//! in `tor-netdoc/src/doc/netstatus/rs/md.rs`
//! imports all of the contents of
//! `tor-netdoc/src/doc/netstatus/rs/each_variety.rs`
//! into the module `doc::netstatus::rs::md`.
//! And, for example, within `rs/each_variety.rs`,
//! `ns_const_name!(FOO)` expands to `MD_FOO`.
//!
//! # Usage
//!
//! Create and include (with `mod`) normal module files:
//! * `vote.rs`
//! * `md.rs`
//! * `plain
//!
//! These contain variety-specific definitions.
//!
//! Alongside these files, create:
//! * `each_flavor.rs`
//! * `each_variety.rs`
//!
//! Do not write a `mod` line for it.
//! Instead, in each of `vote.rs`, `plain.rs` and `md.rs`,
//! call [`ns_do_variety_vote`], [`ns_do_variety_plain`], or [`ns_do_variety_md`].
//!
//! The `each_variety.rs` file will be included three times, once each as a submodule
//! of the variety-specific file.
//! So there will be `...:vote::each_variety::`, etc.,
//! all of which will automatically be re-imported into the parent `vote`.
//! Likewise `each_flavor.rs` file will be included two times.
//!
//! Within `each_variety.rs` (`each_flavor.rs`),
//! all items you define will be triplicated (duplicated).
//! Give them unqualified names.
//! Re-export them in the overall parent module,
//! using `ns_export_each_variety`.
//!
//! # Module scope for `..::VARIETY` and `..::VARIETY::each_variety`/`::each_flavor`
//!
//! Whether to put a particular item in `each_variety.rs`/`each_flavor.rs`, or `VARIETY.rs`,
//! depends just on how similar the source code is for the different varieties.
//!
//! Accordingly there is no real principled distinction between
//! the namespace of each of the (per-variety) `each_variety`/`each_flavor` modules,
//! and their (also per-variety) parents.
//!
//! So `each_variety.rs` and `each_flavor.rs` should `use super::*`.
//! Whenever convenient, fields or private items can be `pub(super)`.
//! `VARIETY.rs` can contain impl blocks for types in `each_variety.rs` and vice versa.
//!
//! `VARIETY.rs` can and should use variety-agnostic names for internal types.
//!
//! Whether an item appears in `each_variety.rs` or `each_flavor.rs`
//! depends (only) on whether it is to be defined for votes, or just for flavours.
//!
//! # Macros for across-variety-variation
//!
//! Within `each_variety.rs`,
//! the following macros are defined for use in `each_variety.rs`/`each_flavor.rs`
//! for variety-dependent elements:
//!
//! * **`ns_ty_name!( BaseTypeName )`**:
//!
//! Expands to `PlainBaseTypeName`, `MdBaseTypeName`, or `VoteBaseTypeName`.
//!
//! Cannot be used to *define* a type.
//! (Define the type with an unqualified name, and
//! re-export it with the qualified name, using `ns_export_each_variety`.)
//!
//! * **`ns_const_name!( BASE_CONST_NAME )`**:
//!
//! Expands to `PLAIN_BASE_CONST_NAME`, `MD_BASE_CONST_NAME`, or `VOTE_BASE_CONST_NAME`.
//!
//! * **`ns_type!( TypeForPlainConsensus, TypeForMdConsensus, [TypeForVote] )`**:
//!
//! Expands to the appropriate one of the two or three specified types.
//! `TypeForVote` may be omitted in `each_flavor.rs`; it is an error in `each_variety.rs`.
//!
//! * **`ns_expr!( value_for_plain_consensus, value_for_md_consensus, [value_for_vote] )`**:
//!
//! Expands to the appropriate one of the two or three specified expressions.
//!
//! * **`ns_choose!( ( FOR PLAIN CONSENSUS.. )( FOR MD CONSENSUS.. )[( FOR VOTE.. )] )`**:
//!
//! Expands to the appropriate one of the two or three specified token streams.
//! (The `( )` surrounding each argument are discarded.)
//!
//! When defining whole items, prefer to put the variety-specific items directly
//! in each of the variety-specific modules.
//!
//! * **`ns_use_this_variety! { use [LHS]::?::{RHS}; }`**:
//!
//! For importing names from variety-specific sibling modules.
//!
//! In the `use` statement, literal `[ ]` are needed around LHS, and are removed.
//! `?` is replaced with the variety abbreviation (`plain`, `md` or `vote).
//!
//! Multiple `use` within the same `ns_use_this_variety!` are allowed.
//! Visibility (`pub use` etc.) is supported.
//!
//! Attributes are not currently supported.
//! Unfortunately, only the form with RHS inside `{ }` is permitted.
//!
//! * **`ns_if_vote!{ ( FOR VOTES.. )( FOR CONSENSUSES.. ) }`**:
//!
//! Expands to `FOR VOTES` or `FOR CONSENSUSES` as applicable,
//! similarly to `ns_choose!`, writing the `FOR CONSENSUSES` part only once.
//! (The `( )` surrounding each argument are discarded.)
//
// Other ways we could have done this:
//
// * Generics: `NetworkStatus<Variety>`.
//
// The generics get everywhere, and they seriously mess up the
// ad-hoc specialisation used for type-based multiplicity dispatch
// (see tor-netdoc/src/parse2/multiplicity.rs).
// We used this scheme for consensuses vs microdescriptor consensuses,
// but it's not workable for votes, so we are switching.
//
// * tt-munching macro_rules macro that filters its input body,
// replacing pseudo-macro invocations with their expansions.
//
// This does work, but it involves radically increasing
// the compiler recursion limit to many thousands.
//
// * custom proc macro(s).
//
// These would probably have to be bespoke to the application.
//
// * build.rs, ad-hoc templating. But this wouldn't be Rust syntax.
/// Includes items from `each_variety.rs` for a particular variety.
///
/// **Internal to `ns_variety_definition_macros.rs`, do not use directly!**
///
/// * `$abbrev` is one of `vote`, `plain`, or `md` as applicable.
///
/// * `: $plain $md $vote $d` is always `: plain md vote $`.
/// `$d` is needed because it's not possible to write a literal `$`
/// in the expansion part of a proc macro, except at the end of a group (!)
/// (`$$` can do that but is is not stable.)
/// `$vote` etc. are needed to match the identifier hygiene of `$abbrev`.
=>
// Each of these macros is redefined per-call site, so may be unused
=>
}
=>
}
=>
}
=>
}
; )* } =>
}
// ----- Now read each_variety.rs in the context with *these* macro definitions -----
// ----- And finally re-export everything into the caller's scope -----
// There might not be any pub items.
pub use *;
ns_if_vote!
} }
/// Select token streams for votes vs conensuses
///
/// See the module-level documentation.
=>
}
/// Include variety-agnostic items, for a full consensus, from `each_variety.rs`.
///
/// Use within `plain.rs`.
=> }
use ns_do_variety_plain;
/// Include variety-agnostic items, for an md consensus, from `each_variety.rs`.
///
/// Use within `md.rs`.
=> }
use ns_do_variety_md;
/// Include variety-agnostic items, for a vote, from `each_variety.rs`.
///
/// Use within `vote.rs`.
// TODO feature = "ns-vote"
=> }
use ns_do_variety_vote;
/// Export variety-specific names from each module.
///
/// Usage:
///
/// ```rust,ignore
/// ns_export_each_variety! {
/// ty: Typename1, Typename2;
/// const: CONSTNAME_1, CONSTNAME_2;
/// }
/// ```
///
/// Exports each `Tyename` as `VarietyTypename`,
/// and each `CONSTNAME` as `VARIETY_CONSTNAME`.
///
/// All three modules `vote`, `plain`, and `md` must exist,
/// and must contain the same items.
//
// TODO consider instead making the variety-specific module names public.
// See https://gitlab.torproject.org/tpo/core/arti/-/merge_requests/3139#note_3239852
=> ;
=> ;
=> ;
=> ;
=> ;
}
/// Export flavor-specific names from each (consensus) module.
///
/// Like [`ns_export_each_variety!`] but only exports for consensuses, not votes.
///
/// The two modules `plain`, and `md` must exist,
/// and must contain the same items.
//
// TODO maybe deduplicate with ns_export_each_variety
=> ;
=> ;
=> ;
=> ;
=> ;
}