1
//! Deriving `NetdocParseable`
2

            
3
use super::*;
4

            
5
/// Helper to implemnet `dtrace!` inside `NetdocParseable` derive-deftly macro.
6
#[doc(hidden)]
7
pub fn netdoc_parseable_derive_debug(ttype: &str, msg: &str, vals: &[&dyn Debug]) {
8
    // Take a lock like this so that all our output appears at once,
9
    // rather than possibly being interleaved with similar output for other types.
10
    let mut out = std::io::stderr().lock();
11
    (|| {
12
        write!(out, "netdoc {ttype} parse: {msg}")?;
13
        for val in vals {
14
            write!(out, ", {val:?}")?;
15
        }
16
        writeln!(out)
17
    })()
18
    .expect("write to stderr failed");
19
}
20

            
21
define_derive_deftly_module! {
22
    /// Common definitions for `NetdocParseable` and `NetdocParseableFields`
23
    ///
24
    ///  * **`THIS_ITEM`**: consumes the next item and evaluates to it as an `UnparsedItem`.
25
    ///    See the definition in `NetdocParseable`.
26
    ///
27
    ///  * **`F_ACCUMULATE_VAR`** the variable or field into which to accumulate
28
    ///    normal items for this field.  Must be of type `&mut $F_ACCUMULATE_TYPE`.
29
    ///
30
    /// Importer must also import `NetdocSomeItemsDeriveCommon` and `NetdocDeriveAnyCommon`.
31
    NetdocSomeItemsParseableCommon beta_deftly:
32

            
33
    // Convenience alias for our prelude
34
    ${define P { $crate::parse2::internal_prelude }}
35

            
36
    // Defines the `dtrace` macro.
37
    ${define DEFINE_DTRACE {
38
        #[allow(unused_macros)]
39
        macro_rules! dtrace { { $$msg:literal $$(, $$val:expr )* $$(,)? } => {
40
          ${if tmeta(netdoc(debug)) {
41
              $P::netdoc_parseable_derive_debug(
42
                  ${concat $ttype},
43
                  $$msg,
44
                  &[ $$( &&$$val as _, )* ],
45
              )
46
          }}
47
        }}
48
    }}
49

            
50
    // The effective field type for parsing.
51
    //
52
    // Handles #[deftly(netdoc(default))], in which case we parse as if the field was Option,
53
    // and substitute in the default at the end.
54
    //
55
    ${define F_EFFECTIVE_TYPE {
56
        ${if all(fmeta(netdoc(default))) {
57
            Option::<$ftype>
58
        } else {
59
            $ftype
60
        }}
61
    }}
62

            
63
    // Provide `$<selector_ $fname>` for every (suitable) field.
64
    ${define ITEM_SET_SELECTORS {
65
        $(
66
          ${when not(any(F_FLATTEN, F_SKIP))}
67

            
68
          // See `mod multiplicity`.
69
        ${if not(all(F_INTRO, fmeta(netdoc(with)))) {
70
          // If the intro it has `with`, we don't check its trait impl, and this ends up unused
71
          let $<selector_ $fname> = $F_SELECTOR_VALUE;
72
        }}
73
        )
74
    }}
75
    // The item set selector for this field.
76
    // We must provide this, rather than expanding $<selector_ $fname> at the use sites,
77
    // because the identifier `selector_` has different macro_rules hygiene here vs there!
78
    // TODO derive-deftly#130
79
5196
    ${define F_SELECTOR $<selector_ $fname>}
80
    // The selector value for this field.  Used where we don't want to bind a selector
81
    // for every field with $ITEM_SET_SELECTORS (and within $ITEM_SET_SELECTORS).
82
    ${define F_SELECTOR_VALUE {( MultiplicitySelector::<$F_EFFECTIVE_TYPE>::default() )}}
83
    // Check that every field type implements the necessary trait.
84
    ${define CHECK_FIELD_TYPES_PARSEABLE {
85
        $(
86
          ${when not(any(F_FLATTEN, F_SKIP))}
87

            
88
          // Expands to `selector_FIELD.check_SOMETHING();`
89
          //
90
          // If the relevant trait isn't implemented, rustc reports the error by
91
          // pointing at the `check-something` call.  We re-span that identifier
92
          // to point to the field name, so that's where the error is reported.
93
          //
94
          // Without this, we just get a report that `item` doesn't implement the required
95
          // trait - but `item` is a local variable here, so the error points into the macro
96
        ${if not(all(any(F_INTRO, F_NORMAL), fmeta(netdoc(with)))) {
97
          $<selector_ $fname> . ${paste_spanned $fname ${select1
98
                  any(F_INTRO, F_NORMAL){
99
                      // For the intro item, this is not completely precise, because the
100
                      // it will allow Option<> and Vec<> which aren't allowed there.
101
                      ${if
102
                        fmeta(netdoc(single_arg)) { check_item_argument_parseable }
103
                        else { check_item_value_parseable }
104
                      }
105
                  }
106
                  F_SIGNATURE { check_signature_item_parseable }
107
                  F_SUBDOC    { check_subdoc_parseable         }
108
          }} ();
109
        }}
110
        )
111
    }}
112

            
113
    // Convert the UnparsedItem (in `item` to the value (to accumulate).
114
    // Expands to an expression.
115
    ${define ITEM_VALUE_FROM_UNPARSED {
116
        ${if fmeta(netdoc(with)) {
117
          ${fmeta(netdoc(with)) as path}
118
              ::${paste_spanned $fname from_unparsed}
119
              (item)?
120
        } else if fmeta(netdoc(single_arg)) { {
121
          let item = ItemValueParseable::from_unparsed(item)?;
122
          let (item,) = item;
123
          item
124
        } } else {
125
          ItemValueParseable::from_unparsed(item)?
126
        }}
127
    }}
128

            
129
    // Type into which we accumulate value(s) of this field
130
    ${define F_ACCUMULATE_TYPE {
131
        ${if F_FLATTEN {
132
            <$ftype as $P::NetdocParseableFields>::Accumulator
133
        } else {
134
            Option::<$F_EFFECTIVE_TYPE>
135
        }
136
    }}}
137

            
138
    // Accumulates `item` (which must be `ItemSetMethods::Each`) into `$F_ACCUMULATE_VAR`
139
    ${define ACCUMULATE_ITEM_VALUE { {
140
        $F_SELECTOR.${paste_spanned $fname accumulate}($F_ACCUMULATE_VAR, item)?;
141
    } }}
142

            
143
    // Handle a nonstructural field, parsing and accumulating its value
144
    //
145
    // Looks at `kw` for the keyword.
146
    //
147
    // Expands to a series of `if ... { ... } else`.
148
    // The use site must provide (maybe further arms) and a fallback block!
149
    //
150
    // If the item is the intro item for this document, evaluates `break` -
151
    // so if `f_INTRO` is not trivially false, must be expanded within a field loop.
152
    ${define NONSTRUCTURAL_ACCUMULATE_ELSE {
153
        ${for fields {
154
          ${when not(any(F_FLATTEN, F_SUBDOC, F_SKIP))}
155

            
156
          if kw == $F_KEYWORD {
157
            ${select1
158
              F_NORMAL {
159
                let item = $THIS_ITEM;
160
                dtrace!("is normal", item);
161
                let item = $ITEM_VALUE_FROM_UNPARSED;
162
                $ACCUMULATE_ITEM_VALUE
163
              }
164
              F_SIGNATURE {
165
                let hash_inputs = input
166
                      .peek_signature_hash_inputs(signed_doc_body)?
167
                      .expect("not eof, we peeked kw");
168

            
169
                let item = $THIS_ITEM;
170
                dtrace!("is signature", item);
171
                let item =
172
                    SignatureItemParseable::from_unparsed_and_body(item, &hash_inputs)?;
173
                $ACCUMULATE_ITEM_VALUE
174
              }
175
              F_INTRO {
176
                dtrace!("is intro", kw);
177
                break;
178
              } // start of next similar document
179
            }
180
          } else
181
        }}
182
        ${for fields {
183
          ${when F_FLATTEN}
184

            
185
          if $ftype::is_item_keyword(kw) {
186
              dtrace!(${concat "is flatten in " $fname}, kw);
187
              let item = $THIS_ITEM;
188
              <$ftype as NetdocParseableFields>::accumulate_item($F_ACCUMULATE_VAR, item)?;
189
          } else
190
        }}
191
    }}
192

            
193
    // Completes a document
194
    //
195
    // The fields accumulated so far must be in `$fpatname` (as a value, not a ref,
196
    // and therefore not in $F_ACCUMULATE_VAR).
197
    //
198
    // Expands to code which resolves the fields, and ends with `Ok(document value)`.
199
    ${define FINISH_RESOLVE {
200
        ${for fields {
201
            ${select1
202
              F_INTRO {}
203
              any(F_NORMAL, F_SIGNATURE) {
204
                  let $fpatname = $F_SELECTOR.finish($fpatname, $F_KEYWORD_REPORT)?;
205
              }
206
              F_FLATTEN {
207
                  let $fpatname = <$ftype as NetdocParseableFields>::finish($fpatname)?;
208
              }
209
              F_SUBDOC {
210
                  let $fpatname = $F_SELECTOR.finish_subdoc($fpatname)?;
211
              }
212
              F_SKIP {
213
                  #[allow(non_snake_case)]
214
                  let $fpatname = Default::default();
215
              }
216
            }
217
        }}
218
        $(
219
            ${when not(any(F_INTRO, F_SKIP))}
220
            // These conditions are mirrored in NetdocSomeItemsEncodableCommon,
221
            // which is supposed to recognise netdoc(default) precisely when we do.
222
          ${if fmeta(netdoc(default)) {
223
            let $fpatname = Option::unwrap_or_default($fpatname);
224
          }}
225
        )
226
        Ok($vpat)
227
    }}
228
}
229

            
230
define_derive_deftly! {
231
    use NetdocDeriveAnyCommon;
232
    use NetdocEntireDeriveCommon;
233
    use NetdocSomeItemsDeriveCommon;
234
    use NetdocSomeItemsParseableCommon;
235

            
236
    /// Derive [`NetdocParseable`] for a document (or sub-document)
237
    ///
238
    // NB there is very similar wording in the NetdocEncodable derive docs.
239
    // If editing any of this derive's documentation, considering editing that too.
240
    //
241
    /// ### Expected input structure
242
    ///
243
    /// Should be applied named-field struct, where each field is
244
    /// an Item which may appear in the document,
245
    /// or a sub-document.
246
    ///
247
    /// The first field will be the document's intro Item.
248
    /// The expected Keyword for each Item will be kebab-case of the field name.
249
    ///
250
    /// ### Field type
251
    ///
252
    /// Each field must be
253
    ///  * `impl `[`ItemValueParseable`] for an "exactly once" field,
254
    ///  * `Vec<T: ItemValueParseable>` for "zero or more", or
255
    ///  * `BTreeSet<T: ItemValueParseable + Ord>`, or
256
    ///  * `Option<T: ItemValueParseable>` for "zero or one".
257
    ///
258
    /// We don't directly support "at least once":
259
    /// the parsed network document doesn't imply the invariant
260
    /// that at least one such item was present.
261
    // We could invent a `NonemptyVec` or something for this.
262
    ///
263
    /// (This is implemented via types in the [`multiplicity`] module,
264
    /// specifically [`ItemSetSelector`].)
265
    ///
266
    /// ### Signed documents
267
    ///
268
    /// To handle signed documents define two structures:
269
    ///
270
    ///  * `Foo`, containing only the content, not the signatures.
271
    ///    Derive `NetdocParseable` and [`NetdocSigned`](derive_deftly_template_NetdocSigned).
272
    ///  * `FooSignatures`, containing only the signatures.
273
    ///    Derive `NetdocParseable` with `#[deftly(netdoc(signatures))]`.
274
    ///
275
    /// Don't mix signature items with non-signature items in the same struct.
276
    /// (This wouldn't compile, because the field type would implement the wrong trait.)
277
    ///
278
    /// ### Top-level attributes:
279
    ///
280
    /// * **`#[deftly(netdoc(doctype_for_error = "EXPRESSION"))]`**:
281
    ///
282
    ///   Specifies the value to be returned from
283
    ///   [`NetdocParseable::doctype_for_error`].
284
    ///
285
    ///   Note, must be an expression, so for a literal, nested `""` are needed.
286
    ///
287
    ///   The default is the intro item keyword.
288
    ///
289
    /// * **`#[deftly(netdoc(signatures))]`**:
290
    ///
291
    ///   This type is the signatures section of another document.
292
    ///   Signature sections have no separate intro keyword:
293
    ///   every field is structural and they are recognised in any order.
294
    ///
295
    ///   Fields must implement [`SignatureItemParseable`],
296
    ///   rather than [`ItemValueParseable`],
297
    ///
298
    ///   This signatures sub-document will typically be included in a
299
    ///   `FooSigned` struct derived with
300
    ///   [`NetdocSigned`](derive_deftly_template_NetdocSigned),
301
    ///   rather than included anywhere manually.
302
    ///
303
    /// * **`#[deftly(netdoc(debug))]`**:
304
    ///
305
    ///   The generated implementation will generate copious debug output
306
    ///   to the program's stderr when it is run.
307
    ///   Do not enable in production!
308
    ///
309
    /// ### Field-level attributes:
310
    ///
311
    /// * **`#[deftly(netdoc(keyword = STR))]`**:
312
    ///
313
    ///   Use `STR` as the Keyword for this Item.
314
    ///
315
    /// * **`#[deftly(netdoc(single_arg))]`**:
316
    ///
317
    ///   The field type implements `ItemArgumentParseable`,
318
    ///   instead of `ItemValueParseable`,
319
    ///   and is parsed as if `(FIELD_TYPE,)` had been written.
320
    ///
321
    /// * **`#[deftly(netdoc(with = "MODULE"))]`**:
322
    ///
323
    ///   Instead of `ItemValueParseable`, the item is parsed with `MODULE::from_unparsed`,
324
    ///   which must have the same signature as [`ItemValueParseable::from_unparsed`].
325
    ///
326
    ///   (Not supported for sub-documents, signature items, or field collections.)
327
    ///
328
    /// * **`#[deftly(netdoc(default))]`**:
329
    ///
330
    ///   This field is optional ("at most once");
331
    ///   if not present, `FIELD_TYPE::default()` will be used.
332
    ///
333
    ///   This is an alternative to declaring the field type as `Option`
334
    ///   With `netdoc(default)`, the field value doesn't need unwrapping.
335
    ///   With `Option` it is possible to see if the field was provided.
336
    ///
337
    /// * **`#[deftly(netdoc(flatten))]`**:
338
    ///
339
    ///   This field is a struct containing further individual normal fields.
340
    ///   The Items for those individual fields can appear in *this*
341
    ///   outer document in any order, interspersed with other normal fields.
342
    ///
343
    ///   The field type must implement [`NetdocParseableFields`].
344
    ///
345
    /// * **`#[deftly(netdoc(skip))]`**:
346
    ///
347
    ///   This field doesn't really appear in the network document.
348
    ///   It won't be recognised during parsing.
349
    ///   Instead, `Default::default()` will be used for the field value.
350
    ///
351
    /// * **`#[deftly(netdoc(subdoc))]`**:
352
    ///
353
    ///   This field is a sub-document.
354
    ///   The value type `T` must implment [`NetdocParseable`]
355
    ///   *instead of* `ItemValueParseable`.
356
    ///
357
    ///   The field name is not used for parsging;
358
    ///   the sub-document's intro keyword is used instead.
359
    ///
360
    ///   Sub-documents are expected to appear after all normal items,
361
    ///   in the order presented in the struct definition.
362
    ///
363
    /// # Example
364
    ///
365
    /// ```
366
    /// use derive_deftly::Deftly;
367
    /// use tor_netdoc::derive_deftly_template_NetdocParseable;
368
    /// use tor_netdoc::derive_deftly_template_NetdocSigned;
369
    /// use tor_netdoc::derive_deftly_template_ItemValueParseable;
370
    /// use tor_netdoc::parse2::{parse_netdoc, ParseInput, VerifyFailed};
371
    /// use tor_netdoc::parse2::{SignatureItemParseable, SignatureHashInputs};
372
    ///
373
    /// #[derive(Deftly, Debug, Clone)]
374
    /// #[derive_deftly(NetdocParseable, NetdocSigned)]
375
    /// pub struct NdThing {
376
    ///     pub thing_start: (),
377
    ///     pub value: (String,),
378
    /// }
379
    ///
380
    /// #[derive(Deftly, Debug, Clone)]
381
    /// #[derive_deftly(NetdocParseable)]
382
    /// #[deftly(netdoc(signatures))]
383
    /// pub struct NdThingSignatures {
384
    ///     pub signature: FoolishSignature,
385
    /// }
386
    ///
387
    /// #[derive(Deftly, Debug, Clone)]
388
    /// #[derive_deftly(ItemValueParseable)]
389
    /// pub struct FoolishSignature {
390
    ///     pub doc_len: usize,
391
    ///
392
    ///     #[deftly(netdoc(sig_hash = "use_length_as_foolish_hash"))]
393
    ///     pub doc_len_actual_pretending_to_be_hash: usize,
394
    /// }
395
    ///
396
    /// fn use_length_as_foolish_hash(body: &SignatureHashInputs) -> usize {
397
    ///     body.body().body().len()
398
    /// }
399
    ///
400
    /// let doc_text =
401
    /// r#"thing-start
402
    /// value something
403
    /// signature 28
404
    /// "#;
405
    ///
406
    /// impl NdThingSigned {
407
    ///     pub fn verify_foolish_timeless(self) -> Result<NdThing, VerifyFailed> {
408
    ///         let sig = &self.signatures.signature;
409
    ///         if sig.doc_len != sig.doc_len_actual_pretending_to_be_hash {
410
    ///             return Err(VerifyFailed::VerifyFailed);
411
    ///         }
412
    ///         Ok(self.body)
413
    ///     }
414
    /// }
415
    ///
416
    /// let input = ParseInput::new(&doc_text, "<input>");
417
    /// let doc: NdThingSigned = parse_netdoc(&input).unwrap();
418
    /// let doc = doc.verify_foolish_timeless().unwrap();
419
    /// assert_eq!(doc.value.0, "something");
420
    /// ```
421
    export NetdocParseable for struct, expect items, beta_deftly:
422

            
423
    ${define F_ACCUMULATE_VAR { (&mut $fpatname) }}
424

            
425
    impl<$tgens> $P::NetdocParseable for $ttype {
426
217
        fn doctype_for_error() -> &'static str {
427
            ${tmeta(netdoc(doctype_for_error)) as expr,
428
4
              default ${concat ${for fields { ${when F_INTRO} $F_KEYWORD_STR }}}}
429
217
        }
430

            
431
11421
        fn is_intro_item_keyword(kw: $P::KeywordRef<'_>) -> bool {
432
            use $P::*;
433

            
434
            ${for fields {
435
                ${when any(F_SIGNATURE, F_INTRO)}
436
11421
                kw == $F_KEYWORD
437
            }}
438
11421
        }
439

            
440
364
        fn is_structural_keyword(kw: $P::KeywordRef<'_>) -> Option<$P::IsStructural> {
441
            #[allow(unused_imports)] // not used if there are no subdocs
442
            use $P::*;
443

            
444
364
            if Self::is_intro_item_keyword(kw) {
445
20
                return Some(IsStructural)
446
344
            }
447

            
448
            ${for fields {
449
                ${when F_SUBDOC}
450
48
                if let y @ Some(_) = $F_SELECTOR_VALUE.is_structural_keyword(kw) {
451
8
                    return y;
452
48
                }
453
            }}
454

            
455
324
            None
456
364
        }
457

            
458
        //##### main parsing function #####
459

            
460
        #[allow(clippy::redundant_locals)] // let item = $THIS_ITEM, which might be item
461
589
        fn from_items<'s>(
462
589
            input: &mut $P::ItemStream<'s>,
463
589
            outer_stop: $P::stop_at!(),
464
589
        ) -> $P::Result<$ttype, $P::ErrorProblem> {
465
            use $P::*;
466
            $DEFINE_DTRACE
467
            $FIELD_ORDERING_CHECK
468

            
469
            //----- prepare item set selectors for every field -----
470
            $ITEM_SET_SELECTORS
471
            $CHECK_FIELD_TYPES_PARSEABLE
472

            
473
            // Is this an intro item keyword ?
474
            //
475
            // Expands to an appropriate `is_intro_item_keyword` method invocation,
476
            // but *without arguments*.  So, something a bit like an expression of type
477
            //    fn(KeywordRef) -> bool
478
            ${define F_SUBDOC_IS_INTRO_ITEM_KEYWORD {
479
                ${if not(F_SUBDOC) { ${error "internal-error: subdoc kw, but not subdoc field"} }}
480
5236
                $F_SELECTOR.is_intro_item_keyword
481
            }}
482

            
483
            //----- Helper fragments for parsing individual pieces of the document -----
484

            
485
            // Peeks a keyword, and returns it but only if it's part of this (sub)doc.
486
            // Return `None` if it was in outer_stop
487
2466
            let peek_keyword = |input: &mut ItemStream<'s>| -> Result<Option<KeywordRef<'s>>, EP> {
488
2464
                let Some(kw) = input.peek_keyword()? else {
489
                    dtrace!("stopping, because EOF");
490
226
                    return Ok(None)
491
                };
492
2238
                if outer_stop.stop_at(kw) {
493
                    dtrace!("stopping, because peeked", kw);
494
266
                    return Ok(None)
495
1972
                }
496
1972
                Ok(Some(kw))
497
2464
            };
498

            
499
            // Returns the actual item as an UnparsedItem, committing to consuming it.
500
            // Can panic if called without previous `peek_keyword`.
501
            ${define THIS_ITEM  {
502
182
                input.next_item()?.expect("peeked")
503
            }}
504

            
505
            //----- keyword classification closures -----
506

            
507
            // Is this a keyword for one of our sub-documents?
508
589
            let is_subdoc_kw = ${for fields {
509
                ${when F_SUBDOC}
510
5196
                StopAt(|kw: KeywordRef<'_>| $F_SUBDOC_IS_INTRO_ITEM_KEYWORD(kw)) |
511
90
              }}
512
509
                StopAt(false)
513
            ;
514
            // Is this a keyword for one of our parents or sub-documents?
515
589
            let inner_stop = outer_stop | is_subdoc_kw;
516

            
517
            //========== actual parsing ==========
518

            
519
            // For each parsing loop/section, where we aren't looking for precisely one thing,
520
            // we should explicitly decide what to do with each of:
521
            //   - F_INTRO - intro item for this document (maybe next instance in parent)
522
            //   - F_NORMAL - normal items
523
            //   - subdocuments, is_subdoc_kw and F_SUBDOC
524
            //   - F_SIGNATURE
525
            //   - our parent's structural keywords, outer_stop
526
            // 5 cases in all.
527

            
528
            // Note the body of the document (before the signatures)
529
          ${if T_SIGNATURES {
530
128
            let signed_doc_body = input.body_sofar_for_signature();
531
          }}
532

            
533
            //----- Parse the intro item, and introduce bindings for the other items. -----
534
            dtrace!("looking for intro item");
535

            
536
          $( ${select1 F_INTRO {
537

            
538
461
            let item = input.next_item()?.ok_or(EP::EmptyDocument)?;
539
            dtrace!("intro", item);
540
455
            if !Self::is_intro_item_keyword(item.keyword()) {
541
51
                Err(EP::WrongDocumentType)?;
542
404
            }
543
388
            let $fpatname: $ftype = $ITEM_VALUE_FROM_UNPARSED;
544

            
545
          } F_SKIP {
546

            
547
          } else {
548

            
549
484
            let mut $fpatname = $F_ACCUMULATE_TYPE::default();
550

            
551
          }})
552

            
553
            //----- Parse the normal items -----
554
            dtrace!("looking for normal items");
555

            
556
2144
            while let Some(kw) = peek_keyword(input)? {
557
                dtrace!("for normal, peeked", kw);
558
1718
                if inner_stop.stop_at(kw) {
559
                    dtrace!("is inner stop", kw);
560
80
                    break;
561
1638
                };
562

            
563
                $NONSTRUCTURAL_ACCUMULATE_ELSE
564
182
                {
565
182
                    dtrace!("is unknown (in normal)");
566
182
                    let _: UnparsedItem = $THIS_ITEM;
567
182
                }
568
            }
569

            
570
            //----- Parse the subdocs, in order -----
571
            dtrace!("looking for subdocs");
572

            
573
          ${for fields {
574
            ${when F_SUBDOC}
575
            dtrace!("looking for subdoc", $F_KEYWORD_REPORT);
576

            
577
            loop {
578
88
                let Some(kw) = peek_keyword(input)? else { break };
579
                dtrace!("for subdoc, peek", kw);
580

            
581
40
                if !$F_SUBDOC_IS_INTRO_ITEM_KEYWORD(kw) {
582
                    dtrace!("is not this subdoc", kw);
583
                    break;
584
40
                };
585

            
586
40
                $F_SELECTOR.can_accumulate(&mut $fpatname)?;
587

            
588
                dtrace!("is this subdoc", kw);
589
40
                let item = NetdocParseable::from_items(input, inner_stop);
590
                dtrace!("parsed this subdoc", item.as_ref().map(|_| ()));
591
40
                let item = item?;
592

            
593
                $ACCUMULATE_ITEM_VALUE
594
            }
595
          }}
596

            
597
            // Resolve all the fields
598
            dtrace!("reached end, resolving");
599

            
600
            $FINISH_RESOLVE
601
589
        }
602
    }
603
}
604

            
605
define_derive_deftly! {
606
    use NetdocDeriveAnyCommon;
607
    use NetdocFieldsDeriveCommon;
608
    use NetdocSomeItemsDeriveCommon;
609
    use NetdocSomeItemsParseableCommon;
610

            
611
    /// Derive [`NetdocParseableFields`] for a struct with individual items
612
    ///
613
    /// Defines a struct `FooNetdocParseAccumulator` to be the
614
    /// `NetdocParseableFields::Accumulator`.
615
    ///
616
    /// Similar to
617
    /// [`#[derive_deftly(NetdocParseable)]`](derive_deftly_template_NetdocParseable),
618
    /// but:
619
    ///
620
    ///  * Derives [`NetdocParseableFields`]
621
    $DOC_NETDOC_FIELDS_DERIVE_SUPPORTED
622
    ///
623
    export NetdocParseableFields for struct , expect items, beta_deftly:
624

            
625
    ${define THIS_ITEM item}
626
    ${define F_ACCUMULATE_VAR { (&mut acc.$fname) }}
627

            
628
    #[doc = ${concat "Partially parsed `" $tname "`"}]
629
    ///
630
    /// Used for [`${concat $P::NetdocParseableFields::Accumulator}`].
631
    #[derive(Default, Debug)]
632
    $tvis struct $<$tname NetdocParseAccumulator><$tdefgens> { $(
633
        $fname: $F_ACCUMULATE_TYPE,
634
    ) }
635

            
636
    impl<$tgens> $P::NetdocParseableFields for $ttype {
637
        type Accumulator = $<$ttype NetdocParseAccumulator>;
638

            
639
168
        fn is_item_keyword(
640
168
            #[allow(unused_variables)] // If there are no fields, this is unused
641
168
            kw: $P::KeywordRef<'_>,
642
168
        ) -> bool {
643
            #[allow(unused_imports)] // false positives in some situations
644
            use $P::*;
645

            
646
          ${for fields {
647
            ${when not(F_FLATTEN)}
648
30
            kw == $F_KEYWORD ||
649
          }}
650
          ${for fields {
651
            ${when F_FLATTEN}
652
6
            <$ftype as NetdocParseableFields>::is_item_keyword(kw) ||
653
          }}
654
            false
655
168
        }
656

            
657
        #[allow(clippy::redundant_locals)] // let item = $THIS_ITEM, which might be item
658
162
        fn accumulate_item(
659
162
            #[allow(unused_variables)] // If there are no fields, this is unused
660
162
            acc: &mut Self::Accumulator,
661
162
            #[allow(unused_variables)] // If there are no fields, this is unused
662
162
            item: $P::UnparsedItem<'_>,
663
162
        ) -> $P::Result<(), $P::ErrorProblem> {
664
            #[allow(unused_imports)] // false positives in some situations
665
            use $P::*;
666
            $DEFINE_DTRACE
667

            
668
            $ITEM_SET_SELECTORS
669
            $CHECK_FIELD_TYPES_PARSEABLE
670

            
671
            #[allow(unused_variables)] // If there are no fields, this is unused
672
162
            let kw = item.keyword();
673

            
674
            $NONSTRUCTURAL_ACCUMULATE_ELSE
675
            {
676
                panic!("accumulate_item called though is_intro_item_keyword returns false");
677
            }
678

            
679
            #[allow(unreachable_code)] // If there are no fields!
680
162
            Ok(())
681
162
        }
682

            
683
54
        fn finish(
684
54
            #[allow(unused_variables)] // If there are no fields, this is unused
685
54
            acc: Self::Accumulator
686
54
        ) -> $P::Result<Self, $P::ErrorProblem> {
687
            #[allow(unused_imports)] // false positives in some situations
688
            use $P::*;
689
            $DEFINE_DTRACE
690

            
691
            dtrace!("finish, resolving");
692

            
693
            $ITEM_SET_SELECTORS
694

            
695
54
         $( let $fpatname = acc.$fname; )
696
            $FINISH_RESOLVE
697
54
        }
698
    }
699
}
700

            
701
define_derive_deftly! {
702
    use NetdocDeriveAnyCommon;
703

            
704
    /// Derive `FooSigned` from `Foo`
705
    ///
706
    /// Apply this derive to the main body struct `Foo`.
707
    ///
708
    /// Usually, provide suitable `.verify_...` methods.
709
    ///
710
    /// The body and signature types have to implement `Clone` and `Debug`.
711
    ///
712
    /// ### Top-level attributes:
713
    ///
714
    /// * **`#[deftly(netdoc(signature = "TYPE"))]`**:
715
    ///   Type of the signature(s) section.
716
    ///
717
    ///   TYPE must implement `NetdocParseable`,
718
    ///   with `is_intro_item_keyword` reporting *every* signature keyword.
719
    ///   Normally this is achieved with
720
    ///   `#[derive_deftly(NetdocParseable)] #[deftly(netdoc(signatures))]`.
721
    ///
722
    $DOC_DEBUG_PLACEHOLDER
723
    ///
724
    /// ### Generated struct
725
    ///
726
    /// ```
727
    /// # struct Foo; struct FooSignatures;
728
    /// pub struct FooSigned {
729
    ///     body: Foo,
730
    ///     pub signatures: FooSignatures,
731
    /// }
732
    ///
733
    /// # #[cfg(all())] { r##"
734
    /// impl NetdocParseable for FooSigned { .. }
735
    /// impl NetdocSigned for FooSigned { .. }
736
    /// # "##; }
737
    /// ```
738
    //
739
    // We don't make this a generic struct because the defining module (crate)
740
    // will want to add verification methods, which means they must define the struct.
741
    export NetdocSigned for struct, expect items, beta_deftly:
742

            
743
    // Convenience alias for our prelude
744
    ${define P { $crate::parse2::internal_prelude }}
745

            
746
    // FooSignatures (type name)
747
60
    ${define SIGS_TYPE { $< ${tmeta(netdoc(signatures)) as ty, default $<$ttype Signatures>} > }}
748
60

            
749
60
    #[doc = ${concat "Signed (unverified) form of [`" $tname "`]"}]
750
60
    ///
751
60
    /// Embodies:
752
60
    ///
753
60
    #[doc = ${concat "  * **[`" $tname "`]**: document body"}]
754
60
    #[doc = ${concat "  * **[`" $SIGS_TYPE "`]**: signatures"}]
755
60
    ///
756
60
    /// If this type was parsed from a document text,
757
60
    /// the signatures have *not* yet been verified.
758
60
    ///
759
60
    /// Use a `.verify_...` method to obtain useable, verified, contents.
760
60
    #[derive(Debug, Clone)]
761
60
    $tvis struct $<$ttype Signed> {
762
60
        /// The actual body
763
60
        //
764
60
        // Misuse is prevented by this field not being public.
765
60
        // It can be accessed only in this module, where the verification functions are.
766
60
        body: $ttype,
767
60

            
768
60
        /// Signatures
769
60
        $tvis signatures: $SIGS_TYPE,
770
60
    }
771
60

            
772
60
    impl<$tgens> $P::NetdocParseable for $<$ttype Signed> {
773
109
        fn doctype_for_error() -> &'static str {
774
109
            $ttype::doctype_for_error()
775
109
        }
776
60

            
777
60
        fn is_intro_item_keyword(kw: $P::KeywordRef<'_>) -> bool {
778
60
            $ttype::is_intro_item_keyword(kw)
779
60
        }
780
60

            
781
90
        fn is_structural_keyword(kw: $P::KeywordRef<'_>) -> Option<$P::IsStructural> {
782
90
            $ttype::is_structural_keyword(kw)
783
90
                .or_else(|| $SIGS_TYPE::is_structural_keyword(kw))
784
60
        }
785

            
786
171
        fn from_items<'s>(
787
171
            input: &mut $P::ItemStream<'s>,
788
171
            outer_stop: $P::stop_at!(),
789
171
        ) -> $P::Result<$<$ttype Signed>, $P::ErrorProblem> {
790
            $EMIT_DEBUG_PLACEHOLDER
791
171
            input.parse_signed(outer_stop)
792
171
        }
793
    }
794

            
795
    impl<$tgens> $P::NetdocSigned for $<$ttype Signed> {
796
        type Body = $ttype;
797
        type Signatures = $SIGS_TYPE;
798
12
        fn inspect_unverified(&self) -> (&Self::Body, &Self::Signatures) {
799
12
            (&self.body, &self.signatures)
800
12
        }
801
61
        fn unwrap_unverified(self) -> (Self::Body, Self::Signatures) {
802
61
            (self.body, self.signatures)
803
61
        }
804
122
        fn from_parts(body: Self::Body, signatures: Self::Signatures) -> Self {
805
122
            Self { body, signatures }
806
122
        }
807
    }
808
}
809

            
810
define_derive_deftly! {
811
    use NetdocDeriveAnyCommon;
812
    use NetdocItemDeriveCommon;
813

            
814
    /// Derive `ItemValueParseable`
815
    ///
816
    // NB there is very similar wording in the ItemValueEncodable derive docs.
817
    // If editing any of this derive's documentation, considering editing that too.
818
    //
819
    /// Fields in the struct are parsed from the keyword line arguments,
820
    /// in the order they appear in the struct.
821
    ///
822
    /// ### Field type
823
    ///
824
    /// Each field should be:
825
    ///
826
    ///  * `impl `[`ItemArgumentParseable`] (one argument),
827
    ///  * `Option<impl ItemArgumentParseable>` (one optional argument),
828
    ///  * `Vec<impl ItemArgumentParseable>` (zero or more arguments), or
829
    ///  * `BTreeSet<impl ItemArgumentParseable + Ord>` (zero or more arguments).
830
    ///
831
    /// `ItemArgumentParseable` can be implemented via `impl FromStr`,
832
    /// by writing `impl NormalItemArgument`.
833
    ///
834
    /// For `Option` or `Vec`, we expect that *if* there are any further arguments,
835
    /// they are for this field.
836
    /// So absence of any optional argument means absence of following arguments,
837
    /// and no arguments can follow a `Vec`.
838
    ///
839
    /// Some Tor netdocs have optional arguments followed by other data,
840
    /// with unclear/ambiguous parsing rules.
841
    /// These cases typically require manual implementation of [`ItemValueParseable`].
842
    ///
843
    /// (Multiplicity is implemented via types in the [`multiplicity`] module,
844
    /// specifically [`ArgumentSetSelector`] and [`ArgumentSetMethods`].)
845
    ///
846
    /// ### Top-level attributes:
847
    ///
848
    ///  * **`#[deftly(netdoc(no_extra_args))]**:
849
    ///
850
    ///    Reject, rather than ignore, additional arguments found in the document
851
    ///    which aren't described by the struct.
852
    ///
853
    /// * **`#[deftly(netdoc(debug))]`**:
854
    ///
855
    ///   Currently implemented only as a placeholde
856
    ///
857
    ///   The generated implementation may in future generate copious debug output
858
    ///   to the program's stderr when it is run.
859
    ///   Do not enable in production!
860
    ///
861
    $DOC_DEBUG_PLACEHOLDER
862
    ///
863
    /// ### Field-level attributes:
864
    ///
865
    ///  * **`#[deftly(netdoc(rest))]**:
866
    ///
867
    ///    The field is the whole rest of the line.
868
    ///    Must come after any other normal argument fields.
869
    ///    Only allowed once.
870
    ///
871
    ///    The field type must implement `FromStr`.
872
    ///    (I.e. `Vec` , `Option` etc., are not allowed, and `ItemArgumentParseable` is not used.)
873
    ///
874
    ///  * **`#[deftly(netdoc(object))]**:
875
    ///
876
    ///    The field is the Object.
877
    ///    It must implement [`ItemObjectParseable`]
878
    ///    (or be `Option<impl ItemObjectParseable>`).
879
    ///
880
    ///    Only allowed once.
881
    ///    If omittted, any object is rejected.
882
    ///
883
    ///  * **`#[deftly(netdoc(object(label = "LABEL")))]**:
884
    ///
885
    ///    Sets the expected label for an Object.
886
    ///    If not supplied, uses [`ItemObjectParseable::check_label`].
887
    ///
888
    ///  * **`#[deftly(netdoc(with = "MODULE")]**:
889
    ///
890
    ///    Instead of `ItemArgumentParseable`, the argument is parsed with `MODULE::from_args`,
891
    ///    which must have the same signature as [`ItemArgumentParseable::from_args`].
892
    ///
893
    ///    With `#[deftly(netdoc(rest))]`, the argument is parsed with `MODULE::from_args_rest`,
894
    ///    must have the signature
895
    ///    `fn from_args_rest(s: &str) -> Result<FIELD, _>`).
896
    ///    and replaces `<FIELD as FromStr>::from_str`.
897
    ///
898
    ///    With `#[deftly(netdoc(object))]`, uses `MODULE::try_from`
899
    ///    which must have the signature `fn(Vec<u8>) -> Result<OBJECT, _>;
900
    ///    like `TryFrom::<Vec<u8>>>::try_from`.
901
    ///    LABEL must also be specified
902
    ///    unless the object also implements `ItemObjectParseable`.
903
    ///    Errors from parsing will all be collapsed into
904
    ///    [`ErrorProblem::ObjectInvalidData`].
905
    ///
906
    ///  * **`#[deftly(netdoc(sig_hash = "HASH_METHOD"))]**:
907
    ///
908
    ///    This item is a signature item.
909
    ///    [`SignatureItemParseable`] will be implemented instead of [`ItemValueParseable`].
910
    ///
911
    ///    This field is a document hash.
912
    ///    The hash will be computed using `HASH_METHOD`,
913
    ///    which will be resolved with `sig_hash_methods::*` in scope.
914
    ///
915
    ///    `fn HASH_METHOD(body: &SignatureHashInputs) -> HASH_FIELD_VALUE`.
916
    export ItemValueParseable for struct, expect items, beta_deftly:
917

            
918
    ${define P { $crate::parse2::internal_prelude }}
919

            
920
    ${define TRAIT ${if T_IS_SIGNATURE { SignatureItemParseable } else { ItemValueParseable }}}
921
    ${define METHOD ${if T_IS_SIGNATURE { from_unparsed_and_body } else { from_unparsed }}}
922

            
923
    impl<$tgens> $P::$TRAIT for $ttype {
924
682
        fn $METHOD<'s>(
925
682
            mut input: $P::UnparsedItem<'s>,
926
682
          ${if T_IS_SIGNATURE {
927
682
            document_body: &SignatureHashInputs<'_>,
928
682
          }}
929
682
        ) -> $P::Result<Self, $P::EP>
930
        {
931
            #[allow(unused_imports)] // false positive when macro is used with prelude in scope
932
            use $P::*;
933

            
934
            $EMIT_DEBUG_PLACEHOLDER
935

            
936
682
            let object = input.object();
937
            #[allow(unused)]
938
682
            let mut args = input.args_mut();
939
          $(
940
652
            let $fpatname = ${select1
941
              F_NORMAL { {
942
118
                  let selector = MultiplicitySelector::<$ftype>::default();
943
                ${if not(fmeta(netdoc(with))) {
944
118
                  selector.${paste_spanned $fname check_argument_value_parseable}();
945
                }}
946
118
                  selector.parse_with(
947
118
                      &mut args,
948
                      ${fmeta(netdoc(with))
949
                        as path,
950
                        default { ItemArgumentParseable }}::${paste_spanned $fname from_args},
951
118
                  ).map_err(args.error_handler(stringify!($fname)))?
952
              } }
953
              F_OBJECT { {
954
558
                  let selector = MultiplicitySelector::<$ftype>::default();
955
631
                  let object = object.map(|object| {
956
522
                      let data = object.decode_data()?;
957
                      ${if fmeta(netdoc(object(label))) {
958
132
                          if object.label() != ${fmeta(netdoc(object(label))) as str} {
959
4
                              return Err(EP::ObjectIncorrectLabel)
960
128
                          }
961
                      } else {
962
388
                          selector.check_label(object.label())?;
963
                      }}
964
                      ${if fmeta(netdoc(with)) {
965
                          ${fmeta(netdoc(with)) as path}::${paste_spanned $fname try_from}
966
128
                              (data)
967
                              .map_err(|_| EP::ObjectInvalidData)
968
                      } else {
969
386
                          selector.${paste_spanned $fname check_object_parseable}();
970
386
                          ItemObjectParseable::from_bytes(&data)
971
                      }}
972
631
                  }).transpose()?;
973
546
                  selector.resolve_option(object)?
974
              } }
975
              F_REST { {
976
                  // consumes `args`, leading to compile error if the rest field
977
                  // isn't last (or is combined with no_extra_args).
978
16
                  let args_consume = args;
979
                  ${if fmeta(netdoc(with)) {
980
                      ${fmeta(netdoc(with)) as path}::${paste_spanned $fname from_args_rest}
981
                  } else {
982
10
                      <$ftype as FromStr>::from_str
983
                  }}
984
10
                      (args_consume.into_remaining())
985
10
                      .map_err(|_| AE::Invalid)
986
10
                      .map_err(args_consume.error_handler(stringify!($fname)))?
987
              } }
988
              F_SIG_HASH { {
989
                  #[allow(unused_imports)]
990
                  use $P::sig_hash_methods::*;
991
120
                  ${fmeta(netdoc(sig_hash)) as path}(&document_body)
992
              } }
993
            };
994
          )
995
          ${if approx_equal({}, $( ${when F_OBJECT} $fname )) {
996
124
            if object.is_some() {
997
                return Err(EP::ObjectUnexpected);
998
124
            }
999
          }}
          ${if tmeta(netdoc(no_extra_args)) {
516
            args.reject_extra_args()?;
          }}
664
            Ok($tname { $( $fname: $fpatname, ) })
682
        }
    }
}