tor_netdoc/parse2/
derive.rs

1//! Deriving `NetdocParseable`
2
3use super::*;
4
5/// Macro to help check that netdoc items in a derive input are in the right order
6///
7/// Used only by the `NetdocParseable` derive-deftly macro.
8#[doc(hidden)]
9#[macro_export]
10macro_rules! netdoc_ordering_check {
11    { } => { compile_error!("netdoc must have an intro item so cannot be empty"); };
12
13    // When we have   K0 P0 K1 P1 ...
14    //   * Check that P0 and P1 have a consistent ordr
15    //   * Continue with   K1 P1 ...
16    // So we check each consecutive pair of fields.
17    { $k0:ident $f0:ident $k1:ident $f1:ident $($rest:tt)* } => {
18        $crate::netdoc_ordering_check! { <=? $k0 $k1 $f1 }
19        $crate::netdoc_ordering_check! { $k1 $f1 $($rest)* }
20    };
21    { $k0:ident $f0:ident } => {}; // finished
22
23    // Individual ordering checks for K0 <=? K1
24    //
25    // We write out each of the allowed this-kind next-kind combinations:
26    { <=? intro     $any:ident $f1:ident } => {};
27    { <=? normal    normal     $f1:ident } => {};
28    { <=? normal    subdoc     $f1:ident } => {};
29    { <=? subdoc    subdoc     $f1:ident } => {};
30    // Not in the allowed list, must be an error:
31    { <=? $k0:ident $k1:ident  $f1:ident } => {
32        compile_error!(concat!(
33            "in netdoc, ", stringify!($k1)," field ", stringify!($f1),
34            " may not come after ", stringify!($k0),
35        ));
36    };
37}
38
39/// Helper to implemnet `dtrace!` inside `NetdocParseable` derive-deftly macro.
40#[doc(hidden)]
41pub fn netdoc_parseable_derive_debug(ttype: &str, msg: &str, vals: &[&dyn Debug]) {
42    let mut out = std::io::stderr().lock();
43    (|| {
44        write!(out, "netdoc {ttype} parse: {msg}")?;
45        for val in vals {
46            write!(out, ", {val:?}")?;
47        }
48        writeln!(out)
49    })()
50    .expect("write to String failed");
51}
52
53define_derive_deftly_module! {
54    /// Common definitions for `NetdocParseable` and `NetdocParseableFields`
55    ///
56    /// Importing template must define these:
57    ///
58    ///  * **`F_INTRO`**, **`F_SUBDOC`**, **`F_SIGNATURE`**
59    ///    conditions for the fundamental field kinds which aren't supported everywhere.
60    ///
61    ///    The `F_FLATTEN` and `F_NORMAL` field type conditions are defined here.
62    ///
63    ///  * **`THIS_ITEM`**: consumes the next item and evaluates to it as an `UnparsedItem`.
64    ///    See the definition in `NetdocParseable`.
65    ///
66    ///  * **`F_ACCUMULATE_VAR`** the variable or field into which to accumulate
67    ///    normal items for this field.  Must be of type `&mut $F_ACCUMULATE_TYPE`.
68    NetdocParseableCommon beta_deftly:
69
70    // Convenience alias for our prelude
71    ${define P { $crate::parse2::internal_prelude }}
72
73    // Defines the `dtrace` macro.
74    ${define DEFINE_DTRACE {
75        #[allow(unused_macros)]
76        macro_rules! dtrace { { $$msg:literal $$(, $$val:expr )* $$(,)? } => {
77          ${if tmeta(netdoc(debug)) {
78              netdoc_parseable_derive_debug(
79                  ${concat $ttype},
80                  $$msg,
81                  &[ $$( &&$$val as _, )* ],
82              )
83          }}
84        }}
85    }}
86
87    // Is this field `flatten`?
88    ${defcond F_FLATTEN fmeta(netdoc(flatten))}
89    // Is this field normal (non-structural)?
90    ${defcond F_NORMAL not(any(F_SIGNATURE, F_INTRO, F_FLATTEN, F_SUBDOC))}
91
92    // Field keyword as `&str`
93    ${define F_KEYWORD_STR { ${concat
94        ${if any(F_FLATTEN, F_SUBDOC) {
95          ${if F_INTRO {
96            ${error "#[deftly(netdoc(subdoc))] and (flatten) not supported for intro items"}
97          } else {
98            // Sub-documents and flattened fields have their keywords inside;
99            // if we ask for the field-based keyword name for one of those then that's a bug.
100            ${error "internal error, subdoc KeywordRef"}
101          }}
102        }}
103        ${fmeta(netdoc(keyword)) as str,
104          default ${concat ${kebab_case $fname}}}
105    }}}
106    // Field keyword as `&str` for debugging and error reporting
107    ${define F_KEYWORD_REPORT {
108        ${if F_SUBDOC { ${concat $fname} }
109             else { $F_KEYWORD_STR }}
110    }}
111    // Field keyword as `KeywordRef`
112    ${define F_KEYWORD { (KeywordRef::new_const($F_KEYWORD_STR)) }}
113
114    // The effective field type for parsing.
115    //
116    // Handles #[deftly(netdoc(default))], in which case we parse as if the field was Option,
117    // and substitute in the default at the end.
118    //
119    ${define F_EFFECTIVE_TYPE {
120        ${if all(fmeta(netdoc(default))) {
121            Option::<$ftype>
122        } else {
123            $ftype
124        }}
125    }}
126
127    // Provide `$<selector_ $fname>` for every (suitable) field.
128    ${define ITEM_SET_SELECTORS {
129        $(
130          ${when not(any(F_FLATTEN))}
131
132          // See `mod multiplicity`.
133        ${if not(all(F_INTRO, fmeta(netdoc(with)))) {
134          // If the intro it has `with`, we don't check its trait impl, and this ends up unused
135          let $<selector_ $fname> = MultiplicitySelector::<$F_EFFECTIVE_TYPE>::default();
136        }}
137        )
138    }}
139    // The item set selector for this field.
140    // We must provide this, rather than expanding $<selector_ $fname> at the use sites,
141    // because the identifier `selector_` has different macro_rules hygiene here vs there!
142    // TODO derive-deftly#130
143    ${define F_SELECTOR $<selector_ $fname>}
144
145    // Check that every field type implements the necessary trait.
146    ${define CHECK_FIELD_TYPES_PARSEABLE {
147        $(
148          ${when not(any(F_FLATTEN))}
149
150          // Expands to `selector_FIELD.check_SOMETHING();`
151          //
152          // If the relevant trait isn't implemented, rustc reports the error by
153          // pointing at the `check-something` call.  We re-span that identifier
154          // to point to the field name, so that's where the error is reported.
155          //
156          // Without this, we just get a report that `item` doesn't implement the required
157          // trait - but `item` is a local variable here, so the error points into the macro
158        ${if not(all(any(F_INTRO, F_NORMAL), fmeta(netdoc(with)))) {
159          $<selector_ $fname> . ${paste_spanned $fname ${select1
160                  any(F_INTRO, F_NORMAL){
161                      // For the intro item, this is not completely precise, because the
162                      // it will allow Option<> and Vec<> which aren't allowed there.
163                      ${if
164                        fmeta(netdoc(single_arg)) { check_item_argument_parseable }
165                        else { check_item_value_parseable }
166                      }
167                  }
168                  F_SIGNATURE { check_signature_item_parseable }
169                  F_SUBDOC    { check_subdoc_parseable         }
170          }} ();
171        }}
172        )
173    }}
174
175    // Convert the UnparsedItem (in `item` to the value (to accumulate).
176    // Expands to an expression.
177    ${define ITEM_VALUE_FROM_UNPARSED {
178        ${if fmeta(netdoc(with)) {
179          ${fmeta(netdoc(with)) as path}
180              ::${paste_spanned $fname from_unparsed}
181              (item)?
182        } else if fmeta(netdoc(single_arg)) { {
183          let item = ItemValueParseable::from_unparsed(item)?;
184          let (item,) = item;
185          item
186        } } else {
187          ItemValueParseable::from_unparsed(item)?
188        }}
189    }}
190
191    // Type into which we accumulate value(s) of this field
192    ${define F_ACCUMULATE_TYPE {
193        ${if F_FLATTEN {
194            <$ftype as NetdocParseableFields>::Accumulator
195        } else {
196            Option::<$F_EFFECTIVE_TYPE>
197        }
198    }}}
199
200    // Accumulates `item` (which must be `ItemSetMethods::Each`) into `$F_ACCUMULATE_VAR`
201    ${define ACCUMULATE_ITEM_VALUE { {
202        $F_SELECTOR.${paste_spanned $fname accumulate}($F_ACCUMULATE_VAR, item)?;
203    } }}
204
205    // Handle a nonstructural field, parsing and accumulating its value
206    //
207    // Looks at `kw` for the keyword.
208    //
209    // Expands to a series of `if ... { ... } else`.
210    // The use site must provide (maybe further arms) and a fallback block!
211    //
212    // If the item is the intro item for this document, evaluates `break` -
213    // so if `f_INTRO` is not trivially false, must be expanded within a field loop.
214    ${define NONSTRUCTURAL_ACCUMULATE_ELSE {
215        ${for fields {
216          ${when not(any(F_FLATTEN, F_SUBDOC))}
217
218          if kw == $F_KEYWORD {
219            ${select1
220              F_NORMAL {
221                let item = $THIS_ITEM;
222                dtrace!("is normal", item);
223                let item = $ITEM_VALUE_FROM_UNPARSED;
224                $ACCUMULATE_ITEM_VALUE
225              }
226              F_SIGNATURE {
227                let hash_inputs = input
228                      .peek_signature_hash_inputs(signed_doc_body)?
229                      .expect("not eof, we peeked kw");
230
231                let item = $THIS_ITEM;
232                dtrace!("is signature", item);
233                let item =
234                    SignatureItemParseable::from_unparsed_and_body(item, &hash_inputs)?;
235                $ACCUMULATE_ITEM_VALUE
236              }
237              F_INTRO {
238                dtrace!("is intro", kw);
239                break;
240              } // start of next similar document
241            }
242          } else
243        }}
244        ${for fields {
245          ${when F_FLATTEN}
246
247          if $ftype::is_item_keyword(kw) {
248              dtrace!(${concat "is flatten in " $fname}, kw);
249              let item = $THIS_ITEM;
250              <$ftype as NetdocParseableFields>::accumulate_item($F_ACCUMULATE_VAR, item)?;
251          } else
252        }}
253    }}
254
255    // Completes a document
256    //
257    // The fields accumulated so far must be in `$fpatname` (as a value, not a ref,
258    // and therefore not in $F_ACCUMULATE_VAR).
259    //
260    // Expands to code which resolves the fields, and ends with `Ok(document value)`.
261    ${define FINISH_RESOLVE {
262        ${for fields {
263            ${select1
264              F_INTRO {}
265              any(F_NORMAL, F_SIGNATURE) {
266                  let $fpatname = $F_SELECTOR.finish($fpatname, $F_KEYWORD_REPORT)?;
267              }
268              F_FLATTEN {
269                  let $fpatname = <$ftype as NetdocParseableFields>::finish($fpatname)?;
270              }
271              F_SUBDOC {
272                  let $fpatname = $F_SELECTOR.finish_subdoc($fpatname)?;
273              }
274            }
275        }}
276        $(
277            ${when not(F_INTRO)}
278          ${if fmeta(netdoc(default)) {
279            let $fpatname = Option::unwrap_or_default($fpatname);
280          }}
281        )
282        Ok($vpat)
283    }}
284}
285
286define_derive_deftly! {
287    use NetdocParseableCommon;
288
289    /// Derive [`NetdocParseable`] for a document (or sub-document)
290    ///
291    /// ### Expected input structure
292    ///
293    /// Should be applied named-field struct, where each field is
294    /// an Item which may appear in the document,
295    /// or a sub-document.
296    ///
297    /// The first field will be the document's intro Item.
298    /// The expected Keyword for each Item will be kebab-case of the field name.
299    ///
300    /// ### Field type
301    ///
302    /// Each field must be
303    ///  * `impl `[`ItemValueParseable`] for an "exactly once" field,
304    ///  * `Vec<T: ItemValueParseable>` for "zero or more", or
305    ///  * `Option<T: ItemValueParseable>` for "zero or one".
306    ///
307    /// We don't directly support "at least once":
308    /// the parsed network document doesn't imply the invariant
309    /// that at least one such item was present.
310    // We could invent a `NonemptyVec` or something for this.
311    ///
312    /// (This is implemented via types in the [`multiplicity`] module,
313    /// specifically [`ItemSetSelector`].)
314    ///
315    /// ### Signed documents
316    ///
317    /// To handle signed documents define two structures:
318    ///
319    ///  * `Foo`, containing only the content, not the signatures.
320    ///    Derive `NetdocParseable` and [`NetdocSigned`](derive_deftly_template_NetdocSigned).
321    ///  * `FooSignatures`, containing only the signatures.
322    ///    Derive `NetdocParseable` with `#[deftly(netdoc(signatures))]`.
323    ///
324    /// Don't mix signature items with non-signature items in the same struct.
325    /// (This wouldn't compile, because the field type would implement the wrong trait.)
326    ///
327    /// ### Top-level attributes:
328    ///
329    /// * **`#[deftly(netdoc(doctype_for_error = "EXPRESSION"))]`**:
330    ///
331    ///   Specifies the value to be returned from
332    ///   [`NetdocParseable::doctype_for_error`].
333    ///
334    ///   Note, must be an expression, so for a literal, nested `""` are needed.
335    ///
336    ///   The default is the intro item keyword.
337    ///
338    /// * **`#[deftly(netdoc(signatures))]`**:
339    ///
340    ///   This type is the signatures section of another document.
341    ///   Signature sections have no separate intro keyword:
342    ///   every field is structural and they are recognised in any order.
343    ///
344    ///   Fields must implement [`SignatureItemParseable`],
345    ///   rather than [`ItemValueParseable`],
346    ///
347    ///   This signatures sub-document will typically be included in a
348    ///   `FooSigned` struct derived with
349    ///   [`NetdocSigned`](derive_deftly_template_NetdocSigned),
350    ///   rather than included anywhere manually.
351    ///
352    /// * **`#[deftly(netdoc(debug))]`**:
353    ///
354    ///   The generated implementation will generate copious debug output
355    ///   to the program's stderr when it is run.
356    ///   Do not enable in production!
357    ///
358    /// ### Field-level attributes:
359    ///
360    /// * **`#[deftly(netdoc(keyword = STR))]`**:
361    ///
362    ///   Use `STR` as the Keyword for this Item.
363    ///
364    /// * **`#[deftly(netdoc(single_arg))]`**:
365    ///
366    ///   The field type implements `ItemArgumentParseable`,
367    ///   instead of `ItemValueParseable`,
368    ///   and is parsed as if `(FIELD_TYPE,)` had been written.
369    ///
370    /// * **`#[deftly(netdoc(with = "MODULE"))]`**:
371    ///
372    ///   Instead of `ItemValueParseable`, the item is parsed with `MODULE::from_unparsed`,
373    ///   which must have the same signature as [`ItemValueParseable::from_unparsed`].
374    ///
375    ///   (Not supported for sub-documents, signature items, or field collections.)
376    ///
377    /// * **`#[deftly(netdoc(default))]`**:
378    ///
379    ///   This field is optional ("at most once");
380    ///   if not present, `FIELD_TYPE::default()` will be used.
381    ///
382    ///   This is an alternative to declaring the field type as `Option`
383    ///   With `netdoc(default)`, the field value doesn't need unwrapping.
384    ///   With `Option` it is possible to see if the field was provided.
385    ///
386    /// * **`#[deftly(netdoc(flatten))]`**:
387    ///
388    ///   This field is a struct containing further individual normal fields.
389    ///   The Items for those individual fields can appear in *this*
390    ///   outer document in any order, interspersed with other normal fields.
391    ///
392    ///   The field type must implement [`NetdocParseableFields`].
393    ///
394    /// * **`#[deftly(netdoc(subdoc))]`**:
395    ///
396    ///   This field is a sub-document.
397    ///   The value type `T` must implment [`NetdocParseable`]
398    ///   *instead of* `ItemValueParseable`.
399    ///
400    ///   The field name is not used for parsging;
401    ///   the sub-document's intro keyword is used instead.
402    ///
403    ///   Sub-documents are expected to appear after all normal items,
404    ///   in the order presented in the struct definition.
405    ///
406    /// # Example
407    ///
408    /// ```
409    /// use derive_deftly::Deftly;
410    /// use tor_netdoc::derive_deftly_template_NetdocParseable;
411    /// use tor_netdoc::derive_deftly_template_NetdocSigned;
412    /// use tor_netdoc::derive_deftly_template_ItemValueParseable;
413    /// use tor_netdoc::parse2::{parse_netdoc, ParseInput, VerifyFailed};
414    /// use tor_netdoc::parse2::{SignatureItemParseable, SignatureHashInputs};
415    ///
416    /// #[derive(Deftly, Debug, Clone)]
417    /// #[derive_deftly(NetdocParseable, NetdocSigned)]
418    /// pub struct NdThing {
419    ///     pub thing_start: (),
420    ///     pub value: (String,),
421    /// }
422    ///
423    /// #[derive(Deftly, Debug, Clone)]
424    /// #[derive_deftly(NetdocParseable)]
425    /// #[deftly(netdoc(signatures))]
426    /// pub struct NdThingSignatures {
427    ///     pub signature: FoolishSignature,
428    /// }
429    ///
430    /// #[derive(Deftly, Debug, Clone)]
431    /// #[derive_deftly(ItemValueParseable)]
432    /// pub struct FoolishSignature {
433    ///     pub doc_len: usize,
434    ///
435    ///     #[deftly(netdoc(sig_hash = "use_length_as_foolish_hash"))]
436    ///     pub doc_len_actual_pretending_to_be_hash: usize,
437    /// }
438    ///
439    /// fn use_length_as_foolish_hash(body: &SignatureHashInputs) -> usize {
440    ///     body.body().body().len()
441    /// }
442    ///
443    /// let doc_text =
444    /// r#"thing-start
445    /// value something
446    /// signature 28
447    /// "#;
448    ///
449    /// impl NdThingSigned {
450    ///     pub fn verify_foolish_timeless(self) -> Result<NdThing, VerifyFailed> {
451    ///         let sig = &self.signatures.signature;
452    ///         if sig.doc_len != sig.doc_len_actual_pretending_to_be_hash {
453    ///             return Err(VerifyFailed::VerifyFailed);
454    ///         }
455    ///         Ok(self.body)
456    ///     }
457    /// }
458    ///
459    /// let input = ParseInput::new(&doc_text, "<input>");
460    /// let doc: NdThingSigned = parse_netdoc(&input).unwrap();
461    /// let doc = doc.verify_foolish_timeless().unwrap();
462    /// assert_eq!(doc.value.0, "something");
463    /// ```
464    export NetdocParseable for struct, expect items, beta_deftly:
465
466    // Predicate for the toplevel
467    ${defcond T_SIGNATURES tmeta(netdoc(signatures))}
468
469    // Predicates for the field kinds
470    ${defcond F_INTRO all(not(T_SIGNATURES), approx_equal($findex, 0))}
471    ${defcond F_SUBDOC fmeta(netdoc(subdoc))}
472    ${defcond F_SIGNATURE T_SIGNATURES} // signatures section documents have only signature fields
473
474    ${define F_ACCUMULATE_VAR { (&mut $fpatname) }}
475
476    impl<$tgens> $P::NetdocParseable for $ttype {
477        fn doctype_for_error() -> &'static str {
478            ${tmeta(netdoc(doctype_for_error)) as expr,
479              default ${concat ${for fields { ${when F_INTRO} $F_KEYWORD_STR }}}}
480        }
481
482        fn is_intro_item_keyword(kw: $P::KeywordRef<'_>) -> bool {
483            use $P::*;
484
485            ${for fields {
486                ${when any(F_SIGNATURE, F_INTRO)}
487                kw == $F_KEYWORD
488            }}
489        }
490
491        //##### main parsing function #####
492
493        fn from_items<'s>(
494            input: &mut $P::ItemStream<'s>,
495            outer_stop: $P::stop_at!(),
496        ) -> $P::Result<$ttype, $P::ErrorProblem> {
497            use $P::*;
498            $DEFINE_DTRACE
499
500            //----- compile-time check that fields are in the right order in the struct -----
501
502            ${if not(T_SIGNATURES) { // signatures structs have only signature fields
503              netdoc_ordering_check! {
504                $(
505                    ${select1
506                      F_INTRO     { intro     }
507                      F_NORMAL    { normal    }
508                      F_FLATTEN   { normal    }
509                      F_SUBDOC    { subdoc    }
510                    }
511                    $fname
512                )
513              }
514            }}
515
516            //----- prepare item set selectors for every field -----
517            $ITEM_SET_SELECTORS
518            $CHECK_FIELD_TYPES_PARSEABLE
519
520            // Is this an intro item keyword ?
521            //
522            // Expands to an appropriate `is_intro_item_keyword` method invocation,
523            // but *without arguments*.  So, something a bit like an expression of type
524            //    fn(KeywordRef) -> bool
525            ${define F_SUBDOC_IS_INTRO_ITEM_KEYWORD {
526                ${if not(F_SUBDOC) { ${error "internal-error: subdoc kw, but not subdoc field"} }}
527                $F_SELECTOR.is_intro_item_keyword
528            }}
529
530            //----- Helper fragments for parsing individual pieces of the document -----
531
532            // Peeks a keyword, and returns it but only if it's part of this (sub)doc.
533            // Return `None` if it was in outer_stop
534            let peek_keyword = |input: &mut ItemStream<'s>| -> Result<Option<KeywordRef<'s>>, EP> {
535                let Some(kw) = input.peek_keyword()? else {
536                    dtrace!("stopping, because EOF");
537                    return Ok(None)
538                };
539                if outer_stop.stop_at(kw) {
540                    dtrace!("stopping, because peeked", kw);
541                    return Ok(None)
542                }
543                Ok(Some(kw))
544            };
545
546            // Returns the actual item as an UnparsedItem, committing to consuming it.
547            // Can panic if called without previous `peek_keyword`.
548            ${define THIS_ITEM  {
549                input.next_item()?.expect("peeked")
550            }}
551
552            //----- keyword classification closures -----
553
554            // Is this a keyword for one of our sub-documents?
555            let is_subdoc_kw = ${for fields {
556                ${when F_SUBDOC}
557                StopAt(|kw: KeywordRef<'_>| $F_SUBDOC_IS_INTRO_ITEM_KEYWORD(kw)) |
558              }}
559                StopAt(false)
560            ;
561            // Is this a keyword for one of our parents or sub-documents?
562            let inner_stop = outer_stop | is_subdoc_kw;
563
564            //========== actual parsing ==========
565
566            // For each parsing loop/section, where we aren't looking for precisely one thing,
567            // we should explicitly decide what to do with each of:
568            //   - F_INTRO - intro item for this document (maybe next instance in parent)
569            //   - F_NORMAL - normal items
570            //   - subdocuments, is_subdoc_kw and F_SUBDOC
571            //   - F_SIGNATURE
572            //   - our parent's structural keywords, outer_stop
573            // 5 cases in all.
574
575            // Note the body of the document (before the signatures)
576          ${if T_SIGNATURES {
577            let signed_doc_body = input.body_sofar_for_signature();
578          }}
579
580            //----- Parse the intro item, and introduce bindings for the other items. -----
581            dtrace!("looking for intro item");
582
583          $( ${select1 F_INTRO {
584
585            let item = input.next_item()?.ok_or(EP::EmptyDocument)?;
586            dtrace!("intro", item);
587            if !Self::is_intro_item_keyword(item.keyword()) {
588                Err(EP::WrongDocumentType)?;
589            }
590            let $fpatname: $ftype = $ITEM_VALUE_FROM_UNPARSED;
591
592          } else {
593
594            let mut $fpatname = $F_ACCUMULATE_TYPE::default();
595
596          }})
597
598            //----- Parse the normal items -----
599            dtrace!("looking for normal items");
600
601            while let Some(kw) = peek_keyword(input)? {
602                dtrace!("for normal, peeked", kw);
603                if inner_stop.stop_at(kw) {
604                    dtrace!("is inner stop", kw);
605                    break;
606                };
607
608                $NONSTRUCTURAL_ACCUMULATE_ELSE
609                {
610                    dtrace!("is unknown (in normal)");
611                    let _: UnparsedItem = $THIS_ITEM;
612                }
613            }
614
615            //----- Parse the subdocs, in order -----
616            dtrace!("looking for subdocs");
617
618          ${for fields {
619            ${when F_SUBDOC}
620            dtrace!("looking for subdoc", $F_KEYWORD_REPORT);
621
622            loop {
623                let Some(kw) = peek_keyword(input)? else { break };
624                dtrace!("for subdoc, peek", kw);
625
626                if !$F_SUBDOC_IS_INTRO_ITEM_KEYWORD(kw) {
627                    dtrace!("is not this subdoc", kw);
628                    break;
629                };
630
631                $F_SELECTOR.can_accumulate(&mut $fpatname)?;
632
633                dtrace!("is this subdoc", kw);
634                let item = NetdocParseable::from_items(input, inner_stop);
635                dtrace!("parsed this subdoc", item.as_ref().map(|_| ()));
636                let item = item?;
637
638                $ACCUMULATE_ITEM_VALUE
639            }
640          }}
641
642            // Resolve all the fields
643            dtrace!("reached end, resolving");
644
645            $FINISH_RESOLVE
646        }
647    }
648}
649
650define_derive_deftly! {
651    use NetdocParseableCommon;
652
653    /// Derive [`NetdocParseableFields`] for a struct with individual items
654    ///
655    /// Defines a struct `FooNetdocParseAccumulator` to be the
656    /// `NetdocParseableFields::Accumulator`.
657    ///
658    /// Similar to
659    /// [`#[derive_deftly(NetdocParseable)]`](derive_deftly_template_NetdocParseable),
660    /// but:
661    ///
662    ///  * Derives [`NetdocParseableFields`]
663    ///  * The input struct can contain only normal non-structural items
664    ///    (so it's not a sub-document with an intro item).
665    ///  * The only attributes supported are the field attributes
666    ///    `#[deftly(netdoc(keyword = STR))]`
667    ///    `#[deftly(netdoc(default))]`
668    ///    `#[deftly(netdoc(single_arg))]`
669    ///    `#[deftly(netdoc(with = "MODULE"))]`
670    ///    `#[deftly(netdoc(flatten))]`
671    export NetdocParseableFields for struct , expect items, beta_deftly:
672
673    ${defcond F_INTRO false}
674    ${defcond F_SUBDOC false}
675    ${defcond F_SIGNATURE false}
676
677    ${define THIS_ITEM item}
678    ${define F_ACCUMULATE_VAR { (&mut acc.$fname) }}
679
680    #[doc = ${concat "Partially parsed `" $tname "`"}]
681    ///
682    /// Used for [`${concat $P::NetdocParseableFields::Accumulator}`].
683    #[derive(Default, Debug)]
684    $tvis struct $<$tname NetdocParseAccumulator><$tdefgens> { $(
685        $fname: $F_ACCUMULATE_TYPE,
686    ) }
687
688    impl<$tgens> $P::NetdocParseableFields for $ttype {
689        type Accumulator = $<$ttype NetdocParseAccumulator>;
690
691        fn is_item_keyword(
692            #[allow(unused_variables)] // If there are no fields, this is unused
693            kw: $P::KeywordRef<'_>,
694        ) -> bool {
695            #[allow(unused_imports)] // false positives in some situations
696            use $P::*;
697
698          ${for fields {
699            ${when not(F_FLATTEN)}
700            kw == $F_KEYWORD ||
701          }}
702          ${for fields {
703            ${when F_FLATTEN}
704            <$ftype as NetdocParseableFields>::is_item_keyword(kw) ||
705          }}
706            false
707        }
708
709        fn accumulate_item(
710            #[allow(unused_variables)] // If there are no fields, this is unused
711            acc: &mut Self::Accumulator,
712            #[allow(unused_variables)] // If there are no fields, this is unused
713            item: $P::UnparsedItem<'_>,
714        ) -> $P::Result<(), $P::ErrorProblem> {
715            #[allow(unused_imports)] // false positives in some situations
716            use $P::*;
717            $DEFINE_DTRACE
718
719            $ITEM_SET_SELECTORS
720            $CHECK_FIELD_TYPES_PARSEABLE
721
722            #[allow(unused_variables)] // If there are no fields, this is unused
723            let kw = item.keyword();
724
725            $NONSTRUCTURAL_ACCUMULATE_ELSE
726            {
727                panic!("accumulate_item called though is_intro_item_keyword returns false");
728            }
729
730            #[allow(unreachable_code)] // If there are no fields!
731            Ok(())
732        }
733
734        fn finish(
735            #[allow(unused_variables)] // If there are no fields, this is unused
736            acc: Self::Accumulator
737        ) -> $P::Result<Self, $P::ErrorProblem> {
738            #[allow(unused_imports)] // false positives in some situations
739            use $P::*;
740            $DEFINE_DTRACE
741
742            dtrace!("finish, resolving");
743
744            $ITEM_SET_SELECTORS
745
746         $( let $fpatname = acc.$fname; )
747            $FINISH_RESOLVE
748        }
749    }
750}
751
752define_derive_deftly! {
753    /// Derive `FooSigned` from `Foo`
754    ///
755    /// Apply this derive to the main body struct `Foo`.
756    ///
757    /// Usually, provide suitable `.verify_...` methods.
758    ///
759    /// The body and signature types have to implement `Clone` and `Debug`.
760    ///
761    /// ### Top-level attributes:
762    ///
763    /// * **`#[deftly(netdoc(signature = "TYPE"))]`**:
764    ///   Type of the signature(s) section.
765    ///
766    ///   TYPE must implement `NetdocParseable`,
767    ///   with `is_intro_item_keyword` reporting *every* signature keyword.
768    ///   Normally this is achieved with
769    ///   `#[derive_deftly(NetdocParseable)] #[deftly(netdoc(signatures))]`.
770    ///
771    /// ### Generated struct
772    ///
773    /// ```
774    /// # struct Foo; struct FooSignatures;
775    /// pub struct FooSigned {
776    ///     body: Foo,
777    ///     pub signatures: FooSignatures,
778    /// }
779    ///
780    /// # #[cfg(all())] { r##"
781    /// impl NetdocParseable for FooSigned { .. }
782    /// impl NetdocSigned for FooSigned { .. }
783    /// # "##; }
784    /// ```
785    //
786    // We don't make this a generic struct because the defining module (crate)
787    // will want to add verification methods, which means they must define the struct.
788    export NetdocSigned for struct, expect items, beta_deftly:
789
790    // Convenience alias for our prelude
791    ${define P { $crate::parse2::internal_prelude }}
792
793    // FooSignatures (type name)
794    ${define SIGS_TYPE { $< ${tmeta(netdoc(signatures)) as ty, default $<$ttype Signatures>} > }}
795
796    #[doc = ${concat "Signed (unverified) form of [`" $tname "`]"}]
797    ///
798    /// Embodies:
799    ///
800    #[doc = ${concat "  * **[`" $tname "`]**: document body"}]
801    #[doc = ${concat "  * **[`" $SIGS_TYPE "`]**: signatures"}]
802    ///
803    /// If this type was parsed from a document text,
804    /// the signatures have *not* yet been verified.
805    ///
806    /// Use a `.verify_...` method to obtain useable, verified, contents.
807    #[derive(Debug, Clone)]
808    $tvis struct $<$ttype Signed> {
809        /// The actual body
810        //
811        // Misuse is prevented by this field not being public.
812        // It can be accessed only in this module, where the verification functions are.
813        body: $ttype,
814
815        /// Signatures
816        $tvis signatures: $SIGS_TYPE,
817    }
818
819    impl<$tgens> $P::NetdocParseable for $<$ttype Signed> {
820        fn doctype_for_error() -> &'static str {
821            $ttype::doctype_for_error()
822        }
823
824        fn is_intro_item_keyword(kw: $P::KeywordRef<'_>) -> bool {
825            $ttype::is_intro_item_keyword(kw)
826        }
827
828        fn from_items<'s>(
829            input: &mut $P::ItemStream<'s>,
830            outer_stop: $P::stop_at!(),
831        ) -> $P::Result<$<$ttype Signed>, $P::ErrorProblem> {
832            input.parse_signed(outer_stop)
833        }
834    }
835
836    impl<$tgens> $P::NetdocSigned for $<$ttype Signed> {
837        type Body = $ttype;
838        type Signatures = $SIGS_TYPE;
839        fn inspect_unverified(&self) -> (&Self::Body, &Self::Signatures) {
840            (&self.body, &self.signatures)
841        }
842        fn unwrap_unverified(self) -> (Self::Body, Self::Signatures) {
843            (self.body, self.signatures)
844        }
845        fn from_parts(body: Self::Body, signatures: Self::Signatures) -> Self {
846            Self { body, signatures }
847        }
848    }
849}
850
851define_derive_deftly! {
852    /// Derive `ItemValueParseable`
853    ///
854    /// Fields in the struct are parsed from the keyword line arguments,
855    /// in the order they appear in the struct.
856    ///
857    /// ### Field type
858    ///
859    /// Each field should be:
860    ///
861    ///  * `impl `[`ItemArgumentParseable`] (one argument),
862    ///  * `Option<impl ItemArgumentParseable>` (one optional argument),
863    ///  * `Vec<impl ItemArgumentParseable>` (zero or more arguments), or
864    ///  * `BTreeSet<impl ItemArgumentParseable + Ord>` (zero or more arguments).
865    ///
866    /// `ItemArgumentParseable` can be implemented via `impl FromStr`,
867    /// by writing `impl NormalItemArgument`.
868    ///
869    /// For `Option` or `Vec`, we expect that *if* there are any further arguments,
870    /// they are for this field.
871    /// So absence of any optional argument means absence of following arguments,
872    /// and no arguments can follow a `Vec`.
873    ///
874    /// Some Tor netdocs have optional arguments followed by other data,
875    /// with unclear/ambiguous parsing rules.
876    /// These cases typically require manual implementation of [`ItemValueParseable`].
877    ///
878    /// (Multiplicity is implemented via types in the [`multiplicity`] module,
879    /// specifically [`ArgumentSetSelector`] and [`ArgumentSetMethods`].)
880    ///
881    /// ### Top-level attributes:
882    ///
883    ///  * **`#[deftly(netdoc(no_extra_args))]**:
884    ///
885    ///    Reject, rather than ignore, additional arguments found in the document
886    ///    which aren't described by the struct.
887    ///
888    /// ### Field-level attributes:
889    ///
890    ///  * **`#[deftly(netdoc(rest))]**:
891    ///
892    ///    The field is the whole rest of the line.
893    ///    Must come after any other normal argument fields.
894    ///    Only allowed once.
895    ///
896    ///    The field type must implement `FromStr`.
897    ///    (I.e. `Vec` , `Option` etc., are not allowed.)
898    ///
899    ///  * **`#[deftly(netdoc(object))]**:
900    ///
901    ///    The field is the Object.
902    ///    It must implement [`ItemObjectParseable`]
903    ///    (or be `Option<impl ItemObjectParseable>`).
904    ///
905    ///    Only allowed once.
906    ///    If omittted, any object is rejected.
907    ///
908    ///  * **`#[deftly(netdoc(object(label = "LABEL")))]**:
909    ///
910    ///    Sets the expected label for an Object.
911    ///    If not supplied, uses [`ItemObjectParseable::check_label`].
912    ///
913    ///  * **`#[deftly(netdoc(with = "MODULE")]**:
914    ///
915    ///    Instead of `ItemArgumentParseable`, the argument is parsed with `MODULE::from_args`,
916    ///    which must have the same signature as [`ItemArgumentParseable::from_args`].
917    ///
918    ///    With `#[deftly(netdoc(rest))]`, FUNCTION replaces
919    ///    `<FIELD AS FromStr>::from_str`.
920    ///
921    ///    With `#[deftly(netdoc(objecte))]`, uses `MODULE::try_from`
922    ///    which must have the signature `fn(Vec<u8>) -> Result<OBJECT, _>;
923    ///    like `TryFrom::<Vec<u8>>>::try_from`.
924    ///    LABEL must also be specified
925    ///    unless the object also implements `ItemObjectParseable`.
926    ///    Errors from parsing will be discarded and replaced with
927    ///    [`ErrorProblem::ObjectInvalidData`].
928    ///
929    ///  * **`#[deftly(netdoc(sig_hash = "HASH_METHOD"))]**:
930    ///
931    ///    This item is a signature item.
932    ///    [`SignatureItemParseable`] will be implemented instead of [`ItemValueParseable`].
933    ///
934    ///    This field is a document hash.
935    ///    The hash will be computed using `HASH_METHOD`,
936    ///    which will be resolved with `sig_hash_methods::*` in scope.
937    ///
938    ///    `fn HASH_METHOD(body: &SignatureHashInputs) -> HASH_FIELD_VALUE`.
939    export ItemValueParseable for struct, expect items, beta_deftly:
940
941    ${define P { $crate::parse2::internal_prelude }}
942
943    ${defcond F_REST fmeta(netdoc(rest))}
944    ${defcond F_OBJECT fmeta(netdoc(object))}
945    ${defcond F_SIG_HASH fmeta(netdoc(sig_hash))}
946    ${defcond F_NORMAL not(any(F_REST, F_OBJECT, F_SIG_HASH))}
947
948    ${defcond T_IS_SIGNATURE not(approx_equal(${for fields { ${when F_SIG_HASH} 1 }}, {}))}
949    ${define TRAIT ${if T_IS_SIGNATURE { SignatureItemParseable } else { ItemValueParseable }}}
950    ${define METHOD ${if T_IS_SIGNATURE { from_unparsed_and_body } else { from_unparsed }}}
951
952    impl<$tgens> $P::$TRAIT for $ttype {
953        fn $METHOD<'s>(
954            mut input: $P::UnparsedItem<'s>,
955          ${if T_IS_SIGNATURE {
956            document_body: &SignatureHashInputs<'_>,
957          }}
958        ) -> $P::Result<Self, $P::EP>
959        {
960            #[allow(unused_imports)] // false positive when macro is used with prelude in scope
961            use $P::*;
962
963            let object = input.object();
964            #[allow(unused)]
965            let mut args = input.args_mut();
966          $(
967            let $fpatname = ${select1
968              F_NORMAL { {
969                  let selector = MultiplicitySelector::<$ftype>::default();
970                ${if not(fmeta(netdoc(with))) {
971                  selector.${paste_spanned $fname check_argument_value_parseable}();
972                }}
973                  selector.parse_with(
974                      &mut args,
975                      ${fmeta(netdoc(with))
976                        as path,
977                        default { ItemArgumentParseable }}::${paste_spanned $fname from_args},
978                  ).map_err(args.error_handler(stringify!($fname)))?
979              } }
980              F_OBJECT { {
981                  let selector = MultiplicitySelector::<$ftype>::default();
982                  let object = object.map(|object| {
983                      let data = object.decode_data()?;
984                      ${if fmeta(netdoc(object(label))) {
985                          if object.label() != ${fmeta(netdoc(object(label))) as str} {
986                              return Err(EP::ObjectIncorrectLabel)
987                          }
988                      } else {
989                          selector.check_label(object.label())?;
990                      }}
991                      ${if fmeta(netdoc(with)) {
992                          ${fmeta(netdoc(with)) as path}::${paste_spanned $fname try_from}
993                              (data)
994                              .map_err(|_| EP::ObjectInvalidData)
995                      } else {
996                          selector.${paste_spanned $fname check_object_parseable}();
997                          ItemObjectParseable::from_bytes(&data)
998                      }}
999                  }).transpose()?;
1000                  selector.resolve_option(object)?
1001              } }
1002              F_REST { {
1003                  // consumes `args`, leading to compile error if the rest field
1004                  // isn't last (or is combined with no_extra_args).
1005                  let args_consume = args;
1006                  ${fmeta(netdoc(with))
1007                    as path,
1008                    default { <$ftype as FromStr>::from_str }}(args_consume.into_remaining())
1009                      .map_err(|_| AE::Invalid)
1010                      .map_err(args_consume.error_handler(stringify!($fname)))?
1011              } }
1012              F_SIG_HASH { {
1013                  #[allow(unused_imports)]
1014                  use $P::sig_hash_methods::*;
1015                  ${fmeta(netdoc(sig_hash)) as path}(&document_body)
1016              } }
1017            };
1018          )
1019          ${if approx_equal({}, $( ${when F_OBJECT} $fname )) {
1020            if object.is_some() {
1021                return Err(EP::ObjectUnexpected);
1022            }
1023          }}
1024          ${if tmeta(netdoc(no_extra_args)) {
1025            args.reject_extra_args()?;
1026          }}
1027            Ok($tname { $( $fname: $fpatname, ) })
1028        }
1029    }
1030}