1
//! Common macro elements for deriving parsers and encoders
2

            
3
use derive_deftly::{define_derive_deftly, define_derive_deftly_module};
4

            
5
define_derive_deftly! {
6
    /// Defines a constructor struct and method
7
    //
8
    // TODO maybe move this out of tor-netdoc, to a lower-level dependency
9
    ///
10
    /// "Constructor" is a more lightweight alternative to the builder pattern.
11
    ///
12
    /// # Comparison to builders
13
    ///
14
    ///  * Suitable for transparent, rather than opaque, structs.
15
    ///  * Missing fields during construction are detected at compile-time.
16
    ///  * Construction is infallible at runtime.
17
    ///  * Making a previously-required field optional is an API break.
18
    ///
19
    /// # Input
20
    ///
21
    ///  * `struct Thing`.  (enums and unions are not supported.)
22
    ///
23
    ///  * Each field must impl `Default` or be annotated `#[deftly(constructor)]`
24
    ///
25
    ///  * `Thing` must contain `#[doc(hidden)] pub __non_exhaustive: ()`
26
    ///    rather than being `#[non_exhaustive]`.
27
    ///    (Because struct literal syntax is not available otherwise.)
28
    ///
29
    /// # Generated items
30
    ///
31
    ///  * **`pub struct ThingConstructor`**:
32
    ///    contains all the required (non-optional) fields from `Thing`.
33
    ///    `ThingConstructor` is `exhaustive`.
34
    ///
35
    ///  * **`fn ThingConstructor::construct(self) -> Thing`**:
36
    ///    fills in all the default values.
37
    ///
38
    ///  * `impl From<ThingConstructor> for Thing`
39
    ///
40
    /// # Attributes
41
    ///
42
    /// ## Field attributes
43
    ///
44
    ///  * **`#[deftly(constructor)]`**:
45
    ///    Include this field in `ThingConstructor`.
46
    ///    The caller must provide a value.
47
    ///
48
    ///  * **`#[deftly(constructor(default = EXPR))]`**:
49
    ///    Instead of `Default::default()`, the default value is EXPR.
50
    ///    EXPR cannot refer to anything in `ThingConstructor`.
51
    //     If we want that we would need to invent a feature for it.
52
    ///
53
    /// # Example
54
    ///
55
    /// ```
56
    /// use derive_deftly::Deftly;
57
    /// use tor_netdoc::derive_deftly_template_Constructor;
58
    ///
59
    /// #[derive(Deftly, PartialEq, Debug)]
60
    /// #[derive_deftly(Constructor)]
61
    /// #[allow(clippy::exhaustive_structs)]
62
    /// pub struct Thing {
63
    ///     /// Required field
64
    ///     #[deftly(constructor)]
65
    ///     pub required: i32,
66
    ///
67
    ///     /// Optional field
68
    ///     pub optional: Option<i32>,
69
    ///
70
    ///     /// Optional field with fixed default
71
    ///     #[deftly(constructor(default = 7))]
72
    ///     pub defaulted: i32,
73
    ///
74
    ///     #[doc(hidden)]
75
    ///     pub __non_exhaustive: (),
76
    /// }
77
    ///
78
    /// let thing = Thing {
79
    ///     optional: Some(23),
80
    ///     ..ThingConstructor {
81
    ///         required: 12,
82
    ///     }.construct()
83
    /// };
84
    ///
85
    /// assert_eq!(
86
    ///     thing,
87
    ///     Thing {
88
    ///         required: 12,
89
    ///         optional: Some(23),
90
    ///         defaulted: 7,
91
    ///         __non_exhaustive: (),
92
    ///     }
93
    /// );
94
    /// ```
95
    ///
96
    /// # Note
97
    export Constructor for struct, meta_quoted rigorous, beta_deftly:
98

            
99
    ${define CONSTRUCTOR_NAME $<$tname Constructor>}
100
    ${define CONSTRUCTOR $<$ttype Constructor>}
101

            
102
    ${defcond F_DEFAULT_EXPR fmeta(constructor(default))}
103
    ${defcond F_DEFAULT_TRAIT not(fmeta(constructor))}
104
    ${defcond F_REQUIRED not(any(F_DEFAULT_EXPR, F_DEFAULT_TRAIT))}
105

            
106
    ${for fields {
107
        ${loop_exactly_1 "need a `__non_exhaustive` field (instead of `#[non_exhaustive]`"}
108
        ${when all(
109
            approx_equal($fname, __non_exhaustive),
110
            approx_equal({}, ${tattrs non_exhaustive}),
111
        )}
112
    }}
113

            
114
    $/// Constructor (required fields) for `$tname`
115
    $///
116
    $/// See [`$tname`].
117
    $///
118
    $/// This constructor struct contains precisely the required fields.
119
    $/// You can make a `$tname` out of it with [`.construct()`]($CONSTRUCTOR_NAME::construct),
120
    $/// or the `From` impl,
121
    $/// and use the result as a basis for further modifications.
122
    $///
123
    $/// # Example
124
    $///
125
    $/// ```rust,ignore
126
    $/// let ${snake_case $tname} = $tname {
127
  ${for fields { ${when any(fmeta(constructor(default)), not(fmeta(constructor)))}
128
    $///     $fname: /* optional field value */,
129
  }}
130
    $///     ..$CONSTRUCTOR_NAME {
131
  ${for fields { ${when not(any(fmeta(constructor(default)), not(fmeta(constructor))))}
132
    $///         $fname: /* required field value */,
133
  }}
134
    $///     }.construct()
135
    $/// };
136
    $/// ```
137
    #[allow(clippy::exhaustive_structs)]
138
    $tvis struct $CONSTRUCTOR_NAME<$tdefgens> where $twheres { $(
139
        ${when F_REQUIRED}
140

            
141
        ${fattrs doc}
142
        $fdefvis $fname: $ftype,
143
    ) }
144

            
145
    impl<$tgens> $CONSTRUCTOR where $twheres {
146
        $/// Construct a minimal `$tname`
147
        $///
148
        $/// In the returned [`$tname`],
149
        $/// optional fields all get the default values.
150
2
        $tvis fn construct(self) -> $ttype {
151
            $tname { $(
152
                #[allow(deprecated)]
153
                $fname: ${select1
154
                    F_REQUIRED {
155
                        self.$fname
156
                    }
157
                    F_DEFAULT_TRAIT {
158
                        <$ftype as ::std::default::Default>::default()
159
                    }
160
                    F_DEFAULT_EXPR {
161
                        ${fmeta(constructor(default)) as expr}
162
                    }
163
                },
164
            ) }
165
        }
166
    }
167

            
168
    impl<$tgens> From<$CONSTRUCTOR> for $ttype where $twheres {
169
        fn from(constructor: $CONSTRUCTOR) -> $ttype {
170
            constructor.construct()
171
        }
172
    }
173
}
174

            
175
define_derive_deftly! {
176
    /// Derive all parsing and encoding for a fixed, constant, string
177
    ///
178
    /// Usually, use the more-cooked [`define_constant_string!`] macro instead.
179
    ///
180
    /// # Input
181
    ///
182
    /// Typically, a unit struct.
183
    /// (Can be applied to any ZST struct that implements `Default`.)
184
    ///
185
    /// # Required attribute
186
    ///
187
    ///  * `#[deftly(constant_string = EXPR))]` where `EXPR` is a `&str` expression.
188
    ///
189
    /// # Generated items
190
    ///
191
    /// Implementations of [`FromStr`, `Display`, and `NormalItemArgument`].
192
    /// Therefore also [`ItemArgument`](crate::encode::ItemArgument)
193
    /// and [`ItemArgumentParseable`](crate::parse2::ItemArgumentParseable).
194
    ///
195
    /// # Example
196
    ///
197
    /// ```
198
    /// use derive_deftly::Deftly;
199
    /// use tor_netdoc::derive_deftly_template_ConstantString;
200
    ///
201
    /// #[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Deftly)]
202
    /// #[derive_deftly(ConstantString)]
203
    /// #[deftly(constant_string = "sha3-256")]
204
    /// #[allow(clippy::exhaustive_structs)]
205
    /// pub struct SharedRandV1AlgName;
206
    /// ```
207
    // TODO DIRMIRROR / TODO DIRAUTH use ConstantString for routerdesc::OverloadGeneralVersion?
208
    // TODO DIRMIRROR / TODO DIRAUTH use ConstantString for routerdesc::AuthCertVersion?
209
    export ConstantString for struct, beta_deftly, meta_quoted retain:
210

            
211
    // Bind `constant`.  Ensures the type is as expected.
212
    ${define LET_CONSTANT {
213
        let constant: &str = ${tmeta(constant_string) as expr};
214
    }}
215

            
216
    ${define FMT { ::std::fmt} }
217
    ${define ERR { $crate::ExpectedConstantString } }
218

            
219
    impl<$tgens> $FMT::Display for $ttype where $twheres {
220
14
        fn fmt(&self, f: &mut $FMT::Formatter) -> $FMT::Result {
221
            $LET_CONSTANT
222
            $FMT::Display::fmt(constant, f)
223
        }
224
    }
225

            
226
    impl<$tgens> ::std::str::FromStr for $ttype where $twheres {
227
        type Err = $ERR;
228

            
229
438
        fn from_str(s: &str) -> ::std::result::Result<Self, $ERR> {
230
            $LET_CONSTANT
231
            if s == constant {
232
                Ok(::std::default::Default::default())
233
            } else {
234
                Err($ERR {
235
                    got: s.to_string(),
236
                    expected: constant,
237
                })
238
            }
239
        }
240
    }
241

            
242
    impl<$tgens> $crate::NormalItemArgument for $ttype where $twheres {}
243
}
244

            
245
/// Define a ZST struct for a fixed, constant, argument string in a netdoc item
246
///
247
/// Convenience macro to define a unit struct, derive many traits,
248
/// and derive
249
/// [`ConstantString`](derive_deftly_template_ConstantString).
250
///
251
/// # Example
252
///
253
/// ```
254
/// use tor_netdoc::define_constant_string;
255
/// use tor_netdoc::derive_deftly_template_ConstantString; // sadly, needed for Reasons
256
///
257
/// define_constant_string! {
258
///     /// The shared random algorithm name for the V1 shared random protocol
259
///     ///
260
///     /// This is a constant string, since the version defines the hash algorithm.
261
///     SharedRandV1AlgName = "sha3-256";
262
/// }
263
///
264
/// assert_eq!("sha3-256".parse::<SharedRandV1AlgName>(), Ok(SharedRandV1AlgName));
265
/// assert_eq!(SharedRandV1AlgName.to_string(), "sha3-256");
266
/// ```
267
///
268
/// # Input
269
///
270
/// ```rust,ignore
271
/// define_constant_string! {
272
///     #[ATTRIBUTES...]
273
///     TYPE_NAME = STRING_LITERAL;
274
/// }
275
/// ```
276
///
277
/// # Generated items
278
///
279
/// ```rust,ignore
280
/// #[ATTRIBUTES...]
281
/// pub struct TYPE_NAME;
282
/// ```
283
///
284
/// Implementations of `Default`, `Debug`, `Clone`, `Copy`,
285
/// `Eq`, `PartialEq`, `Ord`, `PartialOrd`, `Hash`.
286
///
287
/// Implementations of [`FromStr`, `Display`, and `NormalItemArgument`].
288
/// Therefore also [`ItemArgument`](crate::encode::ItemArgument)
289
/// and [`ItemArgumentParseable`](crate::parse2::ItemArgumentParseable).
290
//
291
// This macro exists mostly to encapsulate this astonishing list of traits to derive!
292
#[macro_export]
293
macro_rules! define_constant_string {
294
    {
295
        $( #[ $($attr:tt)* ] )*
296
        $name:ident = $string:expr;
297
    } => {
298
        $( #[ $($attr)* ] )*
299
        #[derive(Default, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
300
        #[derive($crate::derive_deftly::Deftly)]
301
        #[derive_deftly(ConstantString)]
302
        #[deftly(constant_string = ($string))]
303
        #[allow(clippy::exhaustive_structs)]
304
        pub struct $name;
305
    };
306
}
307

            
308
/// Macro to help check that netdoc items in a derive input are in the right order
309
///
310
/// Used only by the `NetdocParseable` derive-deftly macro.
311
#[doc(hidden)]
312
#[macro_export]
313
macro_rules! netdoc_ordering_check {
314
    { } => { compile_error!("netdoc must have an intro item so cannot be empty"); };
315

            
316
    // When we have   K0 P0 K1 P1 ...
317
    //   * Check that P0 and P1 have a consistent ordr
318
    //   * Continue with   K1 P1 ...
319
    // So we check each consecutive pair of fields.
320
    { $k0:ident $f0:ident $k1:ident $f1:ident $($rest:tt)* } => {
321
        $crate::netdoc_ordering_check! { <=? $k0 $k1 $f1 }
322
        $crate::netdoc_ordering_check! { $k1 $f1 $($rest)* }
323
    };
324
    { $k0:ident $f0:ident } => {}; // finished
325

            
326
    // Individual ordering checks for K0 <=? K1
327
    //
328
    // We write out each of the allowed this-kind next-kind combinations:
329
    { <=? intro     $any:ident $f1:ident } => {};
330
    { <=? normal    normal     $f1:ident } => {};
331
    { <=? normal    subdoc     $f1:ident } => {};
332
    { <=? subdoc    subdoc     $f1:ident } => {};
333
    // Not in the allowed list, must be an error:
334
    { <=? $k0:ident $k1:ident  $f1:ident } => {
335
        compile_error!(concat!(
336
            "in netdoc, ", stringify!($k1)," field ", stringify!($f1),
337
            " may not come after ", stringify!($k0),
338
        ));
339
    };
340
}
341

            
342
define_derive_deftly_module! {
343
    /// Common definitions for any netdoc derives
344
    NetdocDeriveAnyCommon beta_deftly:
345

            
346
    // Emit an eprintln with deftly(netdoc(debug)), just so that we don't get surprises
347
    // where someone leaves a (debug) in where it's not implemented, and we later implement it.
348
    ${define EMIT_DEBUG_PLACEHOLDER {
349
        ${if tmeta(netdoc(debug)) {
350
            use std::io::Write as _;
351

            
352
            // This messing about with std::io::stderr() mirrors netdoc_parseable_derive_debug.
353
            // (We could use eprintln! #[test] captures eprintln! but not io::stderr.)
354
            writeln!(
355
                std::io::stderr().lock(),
356
                ${concat "#[deftly(netdoc(debug))] applied to " $tname},
357
            ).expect("write to stderr failed");
358
        }}
359
    }}
360
    ${define DOC_DEBUG_PLACEHOLDER {
361
        /// * **`#[deftly(netdoc(debug))]`**:
362
        ///
363
        ///   Currently implemented only as a placeholder
364
        ///
365
        ///   The generated implementation may in future generate copious debug output
366
        ///   to the program's stderr when it is run.
367
        ///   Do not enable in production!
368
    }}
369
}
370

            
371
define_derive_deftly_module! {
372
    /// Common definitions for derives of structs containing items
373
    ///
374
    /// Used by `NetdocParseable`, `NetdocParseableFields`,
375
    /// `NetdocEncodable` and `NetdocEncodableFields`.
376
    ///
377
    /// Importing template must define these:
378
    ///
379
    ///  * **`F_INTRO`**, **`F_SUBDOC`**, **`F_SIGNATURE`**
380
    ///    conditions for the fundamental field kinds which aren't supported everywhere.
381
    ///
382
    ///    The `F_FLATTEN`, `F_SKIP`, `F_NORMAL` field type conditions are defined here.
383
    ///
384
    /// Importer must also import `NetdocDeriveAnyCommon`.
385
    //
386
    // We have the call sites import the other modules, rather than using them here, because:
387
    //  - This avoids the human reader having to chase breadcrumbs
388
    //    to find out what a particular template is using.
389
    //  - The dependency graph is not a tree, so some things would be included twice
390
    //    and derive-deftly cannot deduplicate them.
391
    NetdocSomeItemsDeriveCommon beta_deftly:
392

            
393
    // Is this field `flatten`?
394
    ${defcond F_FLATTEN fmeta(netdoc(flatten))}
395
    // Is this field `skip`?
396
    ${defcond F_SKIP fmeta(netdoc(skip))}
397
    // Is this field normal (non-structural)?
398
    ${defcond F_NORMAL not(any(F_SIGNATURE, F_INTRO, F_FLATTEN, F_SUBDOC, F_SKIP))}
399

            
400
    // Field keyword as `&str`
401
    ${define F_KEYWORD_STR { ${concat
402
        ${if any(F_FLATTEN, F_SUBDOC, F_SKIP) {
403
          ${if F_INTRO {
404
            ${error "#[deftly(netdoc(subdoc))] (flatten) and (skip) not supported for intro items"}
405
          } else {
406
            // Sub-documents and flattened fields have their keywords inside;
407
            // if we ask for the field-based keyword name for one of those then that's a bug.
408
            ${error "internal error, subdoc or skip KeywordRef"}
409
          }}
410
        }}
411
        ${fmeta(netdoc(keyword)) as str,
412
          default ${concat ${kebab_case $fname}}}
413
    }}}
414
    // Field keyword as `&str` for debugging and error reporting
415
    ${define F_KEYWORD_REPORT ${concat
416
        ${if any(F_FLATTEN, F_SUBDOC, F_SKIP) { $fname }
417
             else { $F_KEYWORD_STR }}
418
    }}
419
    // Field keyword as `KeywordRef`
420
    ${define F_KEYWORD { (KeywordRef::new_const($F_KEYWORD_STR)) }}
421
}
422

            
423
define_derive_deftly_module! {
424
    /// Common definitions for derives of whole network documents
425
    ///
426
    /// Used by `NetdocParseable` and `NetdocEncodable`.
427
    ///
428
    /// Importer must also import `NetdocSomeItemsDeriveCommon` and `NetdocDeriveAnyCommon`.
429
    NetdocEntireDeriveCommon beta_deftly:
430

            
431
    // Predicate for the toplevel
432
    ${defcond T_SIGNATURES false}
433

            
434
    // Predicates for the field kinds
435
    ${defcond F_INTRO approx_equal($findex, 0)}
436
    ${defcond F_SUBDOC fmeta(netdoc(subdoc))}
437
    ${defcond F_SIGNATURE T_SIGNATURES} // signatures section documents have only signature fields
438

            
439
    // compile-time check that fields are in the right order in the struct
440
    ${define FIELD_ORDERING_CHECK {
441
        ${if not(T_SIGNATURES) { // signatures structs have only signature fields
442
          netdoc_ordering_check! {
443
            $(
444
                ${when not(F_SKIP)}
445

            
446
                ${select1
447
                  F_INTRO     { intro     }
448
                  F_NORMAL    { normal    }
449
                  F_FLATTEN   { normal    }
450
                  F_SUBDOC    { subdoc    }
451
                }
452
                $fname
453
            )
454
          }
455
        }}
456
    }}
457
}
458

            
459
define_derive_deftly_module! {
460
    /// Common definitions for derives of flattenable network document fields structs
461
    ///
462
    /// Used by `NetdocParseableFields` and `NetdocEncodableFields`.
463
    ///
464
    /// Importer must also import `NetdocSomeItemsDeriveCommon` and `NetdocDeriveAnyCommon`.
465
    NetdocFieldsDeriveCommon beta_deftly:
466

            
467
    // Predicates for the field kinds, used by NetdocSomeItemsDeriveCommon etc.
468
    ${defcond F_INTRO false}
469
    ${defcond F_SUBDOC false}
470
    ${defcond F_SIGNATURE false}
471

            
472
    ${define DOC_NETDOC_FIELDS_DERIVE_SUPPORTED {
473
        ///  * The input struct can contain only normal non-structural items
474
        ///    (so it's not a sub-document with an intro item).
475
        ///  * The only attributes supported are the field attributes
476
        ///    `#[deftly(netdoc(keyword = STR))]`
477
        ///    `#[deftly(netdoc(default))]`
478
        ///    `#[deftly(netdoc(single_arg))]`
479
        ///    `#[deftly(netdoc(with = MODULE))]`
480
        ///    `#[deftly(netdoc(flatten))]`
481
        ///    `#[deftly(netdoc(skip))]`
482
    }}
483
}
484

            
485
define_derive_deftly_module! {
486
    /// Common definitions for derives of network document item value structs
487
    ///
488
    /// Used by `ItemValueParseable` and `ItemValueEncodable`.
489
    ///
490
    /// Importer must also import `NetdocDeriveAnyCommon`.
491
    NetdocItemDeriveCommon beta_deftly:
492

            
493
    ${defcond F_REST fmeta(netdoc(rest))}
494
    ${defcond F_OBJECT fmeta(netdoc(object))}
495
    ${defcond F_SKIP fmeta(netdoc(skip))}
496
    ${defcond F_NORMAL not(any(F_REST, F_OBJECT, F_SKIP))}
497

            
498
    ${defcond T_IS_SIGNATURE tmeta(netdoc(signature))}
499
}