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! {
54    /// Derive [`NetdocParseable`] for a document (or sub-document)
55    ///
56    /// ### Expected input structure
57    ///
58    /// Should be applied named-field struct, where each field is
59    /// an Item which may appear in the document,
60    /// or a sub-document.
61    ///
62    /// The first field will be the document's intro Item.
63    /// The expected Keyword for each Item will be kebab-case of the field name.
64    ///
65    /// ### Field type
66    ///
67    /// Each field must be
68    ///  * `impl `[`ItemValueParseable`] for an "exactly once" field,
69    ///  * `Vec<T: ItemValueParseable>` for "zero or more", or
70    ///  * `Option<T: ItemValueParseable>` for "zero or one".
71    ///
72    /// We don't directly support "at least once":
73    /// the parsed network document doesn't imply the invariant
74    /// that at least one such item was present.
75    // We could invent a `NonemptyVec` or something for this.
76    ///
77    /// (This is implemented via types in the [`multiplicity`] module,
78    /// specifically [`ItemSetSelector`].)
79    ///
80    /// ### Signed documents
81    ///
82    /// To handle signed documents define two structures:
83    ///
84    ///  * `Foo`, containing only the content, not the signatures.
85    ///    Derive `NetdocParseable` and [`NetdocSigned`](derive_deftly_template_NetdocSigned).
86    ///  * `FooSignatures`, containing only the signatures.
87    ///    Derive `NetdocParseable` with `#[deftly(netdoc(signatures))]`.
88    ///
89    /// Don't mix signature items with non-signature items in the same struct.
90    /// (This wouldn't compile, because the field type would implement the wrong trait.)
91    ///
92    /// ### Top-level attributes:
93    ///
94    /// * **`#[deftly(netdoc(doctype_for_error = "EXPRESSION"))]`**:
95    ///
96    ///   Specifies the value to be returned from
97    ///   [`NetdocParseable::doctype_for_error`].
98    ///
99    ///   Note, must be an expression, so for a literal, nested `""` are needed.
100    ///
101    ///   The default is the intro item keyword.
102    ///
103    /// * **`#[deftly(netdoc(signatures))]`**:
104    ///
105    ///   This type is the signatures section of another document.
106    ///   Signature sections have no separate intro keyword:
107    ///   every field is structural and they are recognised in any order.
108    ///
109    ///   Fields must implement [`SignatureItemParseable`],
110    ///   rather than [`ItemValueParseable`],
111    ///
112    ///   This signatures sub-document will typically be included in a
113    ///   `FooSigned` struct derived with
114    ///   [`NetdocSigned`](derive_deftly_template_NetdocSigned),
115    ///   rather than included anywhere manually.
116    ///
117    /// * **`#[deftly(netdoc(debug))]`**:
118    ///
119    ///   The generated implementation will generate copious debug output
120    ///   to the program's stderr when it is run.
121    ///   Do not enable in production!
122    ///
123    /// ### Field-level attributes:
124    ///
125    /// * **`#[deftly(netdoc(keyword = STR))]`**:
126    ///
127    ///   Use `STR` as the Keyword for this Item.
128    ///
129    /// * **`#[deftly(netdoc(default))]`**:
130    ///
131    ///   This field is optional ("at most once");
132    ///   if not present, `FIELD_TYPE::default()` will be used.
133    ///
134    ///   This is an alternative to declaring the field type as `Option`
135    ///   With `netdoc(default)`, the field value doesn't need unwrapping.
136    ///   With `Option` it is possible to see if the field was provided.
137    ///
138    /// * **`#[deftly(netdoc(subdoc))]`**:
139    ///
140    ///   This field is a sub-document.
141    ///   The value type `T` must implment [`NetdocParseable`]
142    ///   *instead of* `ItemValueParseable`.
143    ///
144    ///   The field name is not used for parsging;
145    ///   the sub-document's intro keyword is used instead.
146    ///
147    ///   Sub-documents are expected to appear after all normal items,
148    ///   in the order presented in the struct definition.
149    ///
150    /// # Example
151    ///
152    /// ```
153    /// use derive_deftly::Deftly;
154    /// use tor_netdoc::derive_deftly_template_NetdocParseable;
155    /// use tor_netdoc::derive_deftly_template_NetdocSigned;
156    /// use tor_netdoc::derive_deftly_template_ItemValueParseable;
157    /// use tor_netdoc::parse2::{parse_netdoc, VerifyFailed};
158    /// use tor_netdoc::parse2::{SignatureItemParseable, SignatureHashInputs};
159    ///
160    /// #[derive(Deftly, Debug, Clone)]
161    /// #[derive_deftly(NetdocParseable, NetdocSigned)]
162    /// pub struct NdThing {
163    ///     pub thing_start: (),
164    ///     pub value: (String,),
165    /// }
166    ///
167    /// #[derive(Deftly, Debug, Clone)]
168    /// #[derive_deftly(NetdocParseable)]
169    /// #[deftly(netdoc(signatures))]
170    /// pub struct NdThingSignatures {
171    ///     pub signature: FoolishSignature,
172    /// }
173    ///
174    /// #[derive(Deftly, Debug, Clone)]
175    /// #[derive_deftly(ItemValueParseable)]
176    /// pub struct FoolishSignature {
177    ///     pub doc_len: usize,
178    ///
179    ///     #[deftly(netdoc(sig_hash = "use_length_as_foolish_hash"))]
180    ///     pub doc_len_actual_pretending_to_be_hash: usize,
181    /// }
182    ///
183    /// fn use_length_as_foolish_hash(body: &SignatureHashInputs) -> usize {
184    ///     body.body().body().len()
185    /// }
186    ///
187    /// let doc_text =
188    /// r#"thing-start
189    /// value something
190    /// signature 28
191    /// "#;
192    ///
193    /// impl NdThingSigned {
194    ///     pub fn verify_foolish_timeless(self) -> Result<NdThing, VerifyFailed> {
195    ///         let sig = &self.signatures.signature;
196    ///         if sig.doc_len != sig.doc_len_actual_pretending_to_be_hash {
197    ///             return Err(VerifyFailed::VerifyFailed);
198    ///         }
199    ///         Ok(self.body)
200    ///     }
201    /// }
202    ///
203    /// let doc: NdThingSigned = parse_netdoc(&doc_text, "<input>").unwrap();
204    /// let doc = doc.verify_foolish_timeless().unwrap();
205    /// assert_eq!(doc.value.0, "something");
206    /// ```
207    export NetdocParseable for struct, expect items, beta_deftly:
208
209    // Convenience alias for our prelude
210    ${define P { $crate::parse2::internal_prelude }}
211
212    // Predicate for the toplevel
213    ${defcond T_SIGNATURES tmeta(netdoc(signatures))}
214
215    // Predicates for the field kinds
216    ${defcond F_INTRO all(not(T_SIGNATURES), approx_equal($findex, 0))}
217    ${defcond F_SUBDOC fmeta(netdoc(subdoc))}
218    ${defcond F_SIGNATURE T_SIGNATURES} // signatures section documents have only signature fields
219    ${defcond F_NORMAL not(any(F_SIGNATURE, F_INTRO, F_SUBDOC))}
220
221    // Field keyword as `&str`
222    ${define F_KEYWORD_STR { ${concat
223        ${if F_SUBDOC {
224            // Sub-documents have their own keywords; if we ask for the field-based
225            // keyword name of a sub-document, then that's a bug.
226            ${error "internal error, subdoc KeywordRef"}
227        }}
228        ${fmeta(netdoc(keyword)) as str,
229          default ${concat ${kebab_case $fname}}}
230    }}}
231    // Field keyword as `&str` for debugging and error reporting
232    ${define F_KEYWORD_REPORT {
233        ${if F_SUBDOC { ${concat $fname} }
234             else { $F_KEYWORD_STR }}
235    }}
236    // Field keyword as `KeywordRef`
237    ${define F_KEYWORD { (KeywordRef::new_const($F_KEYWORD_STR)) }}
238
239    // The effective field type for parsing.
240    //
241    // Handles #[deftly(netdoc(default))], in which case we parse as if the field was Option,
242    // and substitute in the default at the end.
243    ${define F_EFFECTIVE_TYPE {
244        ${if all(fmeta(netdoc(default)), not(F_INTRO)) {
245            Option::<$ftype>
246        } else {
247            $ftype
248        }}
249    }}
250
251    impl<$tgens> $P::NetdocParseable for $ttype {
252        fn doctype_for_error() -> &'static str {
253            ${tmeta(netdoc(doctype_for_error)) as expr,
254              default ${concat ${for fields { ${when F_INTRO} $F_KEYWORD_STR }}}}
255        }
256
257        fn is_intro_item_keyword(kw: $P::KeywordRef<'_>) -> bool {
258            use $P::*;
259
260            ${for fields {
261                ${when any(F_SIGNATURE, F_INTRO)}
262                kw == $F_KEYWORD
263            }}
264        }
265
266        //##### main parsing function #####
267
268        fn from_items<'s>(
269            input: &mut $P::ItemStream<'s>,
270            outer_stop: $P::stop_at!(),
271        ) -> Result<$ttype, $P::ErrorProblem> {
272            use $P::*;
273
274            //----- compile-time check that fields are in the right order in the struct -----
275
276            ${if not(T_SIGNATURES) { // signatures structs have only signature fields
277              netdoc_ordering_check! {
278                $(
279                    ${select1
280                      F_INTRO     { intro     }
281                      F_NORMAL    { normal    }
282                      F_SUBDOC    { subdoc    }
283                    }
284                    $fname
285                )
286              }
287            }}
288
289            //----- Debugging -----
290
291            macro_rules! dtrace { { $$msg:literal $$(, $$val:expr )* $$(,)? } => {
292              ${if tmeta(netdoc(debug)) {
293                  netdoc_parseable_derive_debug(
294                      ${concat $ttype},
295                      $$msg,
296                      &[ $$( &&$$val as _, )* ],
297                  )
298              }}
299            }}
300
301            //----- prepare item set selectors for every field -----
302
303          $(
304            ${when not(F_INTRO)}
305
306            // See `mod multiplicity`.
307            let $<selector_ $fname> = ItemSetSelector::<$F_EFFECTIVE_TYPE>::default();
308          )
309
310            // Is this an intro item keyword ?
311            //
312            // Expands to an appropriate `is_intro_item_keyword` method invocation,
313            // but *without arguments*.  So, something a bit like an expression of type
314            //    fn(KeywordRef) -> bool
315            ${define F_SUBDOC_IS_INTRO_ITEM_KEYWORD {
316                ${if not(F_SUBDOC) { ${error "internal-error: subdoc kw, but not subdoc field"} }}
317                $<selector_ $fname>.is_intro_item_keyword
318            }}
319
320            //----- Helper fragments for parsing individual pieces of the document -----
321
322            // Peeks a keyword, and returns it but only if it's part of this (sub)doc.
323            // Return `None` if it was in outer_stop
324            let peek_keyword = |input: &mut ItemStream<'s>| -> Result<Option<KeywordRef<'s>>, EP> {
325                let Some(kw) = input.peek_keyword()? else {
326                    dtrace!("stopping, because EOF");
327                    return Ok(None)
328                };
329                if outer_stop.stop_at(kw) {
330                    dtrace!("stopping, because peeked", kw);
331                    return Ok(None)
332                }
333                Ok(Some(kw))
334            };
335
336            // Returns the actual item as an UnparsedItem, committing to consuming it.
337            // Can panic if called without previous `peek_keyword`.
338            ${define THIS_ITEM  {
339                input.next_item()?.expect("peeked")
340            }}
341
342            // Accumulates `item` (which must be DataSet::Value) into `Putnam`
343            ${define ACCUMULATE_ITEM_VALUE { {
344                $<selector_ $fname>.accumulate(&mut $fpatname, item)?;
345            } }}
346
347            //----- keyword classification closures -----
348
349            // Is this a keyword for one of our sub-documents?
350            let is_subdoc_kw = ${for fields {
351                ${when F_SUBDOC}
352                StopAt(|kw: KeywordRef<'_>| $F_SUBDOC_IS_INTRO_ITEM_KEYWORD(kw)) |
353              }}
354                StopAt(false)
355            ;
356            // Is this a keyword for one of our parents or sub-documents?
357            let inner_stop = outer_stop | is_subdoc_kw;
358
359            //========== actual parsing ==========
360
361            // For each parsing loop/section, where we aren't looking for precisely one thing,
362            // we should explicitly decide what to do with each of:
363            //   - F_INTRO - intro item for this document (maybe next instance in parent)
364            //   - F_NORMAL - normal items
365            //   - subdocuments, is_subdoc_kw and F_SUBDOC
366            //   - F_SIGNATURE
367            //   - our parent's structural keywords, outer_stop
368            // 5 cases in all.
369
370            // Note the body of the document (before the signatures)
371          ${if T_SIGNATURES {
372            let signed_doc_body = input.body_sofar_for_signature();
373          }}
374
375            //----- Parse the intro item, and introduce bindings for the other items. -----
376            dtrace!("looking for intro item");
377
378          $( ${select1 F_INTRO {
379
380            let item = input.next_item()?.ok_or(EP::EmptyDocument)?;
381            dtrace!("intro", item);
382            if !Self::is_intro_item_keyword(item.keyword()) {
383                Err(EP::WrongDocumentType)?;
384            }
385            let $fpatname: $ftype = <$ftype as ItemValueParseable>::from_unparsed(item)?;
386
387          } else {
388
389            let mut $fpatname: Option<$F_EFFECTIVE_TYPE> = None;
390
391          }})
392
393            //----- Parse the normal items -----
394            dtrace!("looking for normal items");
395
396            while let Some(kw) = peek_keyword(input)? {
397                dtrace!("for normal, peeked", kw);
398                if inner_stop.stop_at(kw) {
399                    dtrace!("is inner stop", kw);
400                    break;
401                };
402              ${for fields {
403                ${when not(F_SUBDOC)}
404
405                if kw == $F_KEYWORD {
406                  ${select1
407                    F_NORMAL {
408                      let item = $THIS_ITEM;
409                      dtrace!("is normal", item);
410                      let item = ItemValueParseable::from_unparsed(item)?;
411                      $ACCUMULATE_ITEM_VALUE
412                    }
413                    F_SIGNATURE {
414                      let hash_inputs = input
415                            .peek_signature_hash_inputs(signed_doc_body)?
416                            .expect("not eof, we peeked kw");
417
418                      let item = $THIS_ITEM;
419                      dtrace!("is signature", item);
420                      let item =
421                          SignatureItemParseable::from_unparsed_and_body(item, &hash_inputs)?;
422                      $ACCUMULATE_ITEM_VALUE
423                    }
424                    F_INTRO {
425                      dtrace!("is intro", kw);
426                      break;
427                    } // start of next similar document
428                  }
429                } else
430              }}
431                {
432                    dtrace!("is unknown (in normal)");
433                    let _: UnparsedItem = $THIS_ITEM;
434                }
435            }
436
437            //----- Parse the subdocs, in order -----
438            dtrace!("looking for subdocs");
439
440          ${for fields {
441            ${when F_SUBDOC}
442            dtrace!("looking for subdoc", $F_KEYWORD_REPORT);
443
444            loop {
445                let Some(kw) = peek_keyword(input)? else { break };
446                dtrace!("for subdoc, peek", kw);
447
448                if !$F_SUBDOC_IS_INTRO_ITEM_KEYWORD(kw) {
449                    dtrace!("is not this subdoc", kw);
450                    break;
451                };
452
453                dtrace!("is this subdoc", kw);
454                let item = NetdocParseable::from_items(input, inner_stop);
455                dtrace!("parsed this subdoc", item.as_ref().map(|_| ()));
456                let item = item?;
457
458                $ACCUMULATE_ITEM_VALUE
459            }
460          }}
461
462            // Resolve all the fields
463            dtrace!("reached end, resolving");
464
465          $(
466            ${when not(any(F_INTRO, F_SUBDOC))}
467            let $fpatname = $<selector_ $fname>.finish($fpatname, $F_KEYWORD_REPORT)?;
468          )
469          $(
470            ${when F_SUBDOC}
471            let $fpatname = $<selector_ $fname>.finish_subdoc($fpatname)?;
472          )
473          $(
474            ${when not(F_INTRO)}
475          ${if fmeta(netdoc(default)) {
476            let $fpatname = Option::unwrap_or_default($fpatname);
477          }}
478          )
479
480            let r = $vpat;
481
482            Ok(r)
483        }
484    }
485}
486
487define_derive_deftly! {
488    /// Derive `FooSigned` from `Foo`
489    ///
490    /// Apply this derive to the main body struct `Foo`.
491    ///
492    /// Usually, provide suitable `.verify_...` methods.
493    ///
494    /// The body and signature types have to implement `Clone` and `Debug`.
495    ///
496    /// ### Top-level attributes:
497    ///
498    /// * **`#[deftly(netdoc(signature = "TYPE"))]`**:
499    ///   Type of the signature(s) section.
500    ///
501    ///   TYPE must implement `NetdocParseable`,
502    ///   with `is_intro_item_keyword` reporting *every* signature keyword.
503    ///   Normally this is achieved with
504    ///   `#[derive_deftly(NetdocParseable)] #[deftly(netdoc(signatures))]`.
505    ///
506    /// ### Generated struct
507    ///
508    /// ```
509    /// # struct Foo; struct FooSignatures;
510    /// pub struct FooSigned {
511    ///     body: Foo,
512    ///     pub signatures: FooSignatures,
513    /// }
514    ///
515    /// # #[cfg(all())] { r##"
516    /// impl NetdocParseable for FooSigned { .. }
517    /// impl NetdocSigned for FooSigned { .. }
518    /// # "##; }
519    /// ```
520    //
521    // We don't make this a generic struct because the defining module (crate)
522    // will want to add verification methods, which means they must define the struct.
523    export NetdocSigned expect items, beta_deftly:
524
525    // Convenience alias for our prelude
526    ${define P { $crate::parse2::internal_prelude }}
527
528    // FooSignatures (type name)
529    ${define SIGS_TYPE { $< ${tmeta(netdoc(signatures)) as ty, default $<$ttype Signatures>} > }}
530
531    #[doc = ${concat "Signed (unverified) form of [`" $tname "`]"}]
532    ///
533    /// Embodies:
534    ///
535    #[doc = ${concat "  * **[`" $tname "`]**: document body"}]
536    #[doc = ${concat "  * **[`" $SIGS_TYPE "`]**: signatures"}]
537    ///
538    /// If this type was parsed from a document text,
539    /// the signatures have *not* yet been verified.
540    ///
541    /// Use a `.verify_...` method to obtain useable, verified, contents.
542    #[derive(Debug, Clone)]
543    $tvis struct $<$ttype Signed> {
544        /// The actual body
545        //
546        // Misuse is prevented by this field not being public.
547        // It can be accessed only in this module, where the verification functions are.
548        body: $ttype,
549
550        /// Signatures
551        $tvis signatures: $SIGS_TYPE,
552    }
553
554    impl<$tgens> $P::NetdocParseable for $<$ttype Signed> {
555        fn doctype_for_error() -> &'static str {
556            $ttype::doctype_for_error()
557        }
558
559        fn is_intro_item_keyword(kw: $P::KeywordRef<'_>) -> bool {
560            $ttype::is_intro_item_keyword(kw)
561        }
562
563        fn from_items<'s>(
564            input: &mut $P::ItemStream<'s>,
565            outer_stop: $P::stop_at!(),
566        ) -> Result<$<$ttype Signed>, $P::ErrorProblem> {
567            input.parse_signed(outer_stop)
568        }
569    }
570
571    impl<$tgens> $P::NetdocSigned for $<$ttype Signed> {
572        type Body = $ttype;
573        type Signatures = $SIGS_TYPE;
574        fn inspect_unverified(&self) -> (&Self::Body, &Self::Signatures) {
575            (&self.body, &self.signatures)
576        }
577        fn unwrap_unverified(self) -> (Self::Body, Self::Signatures) {
578            (self.body, self.signatures)
579        }
580        fn from_parts(body: Self::Body, signatures: Self::Signatures) -> Self {
581            Self { body, signatures }
582        }
583    }
584}
585
586define_derive_deftly! {
587    /// Derive `ItemValueParseable`
588    ///
589    /// Fields in the struct are parsed from the keyword line arguments,
590    /// in the order they appear in the struct.
591    ///
592    /// ### Field type
593    ///
594    /// Each field should be:
595    ///
596    ///  * `impl `[`ItemArgumentParseable`] (one argument),
597    ///  * `Option<impl ItemArgumentParseable>` (one optional argument), or
598    ///  * `Vec<impl ItemArgumentParseable>` (zero or more arguments).
599    ///
600    /// `ItemArgumentParseable` is implemented for every `impl FromStr`,
601    /// so `impl FromStr`, `Option<impl FromStr>` and `Vec<impl FromStr>`
602    /// are supported.
603    ///
604    /// For `Option` or `Vec`, we expect that *if* there are any further arguments,
605    /// they are for this field.
606    /// So absence of any optional argument means absence of following arguments,
607    /// and no arguments can follow a `Vec`.
608    ///
609    /// Some Tor netdocs have optional arguments followed by other data,
610    /// with unclear/ambiguous parsing rules.
611    /// These cases typically require manual implementation of [`ItemValueParseable`].
612    ///
613    /// (Multiplicity is implemented via types in the [`multiplicity`] module,
614    /// specifically [`ArgumentSetSelector`] and [`ArgumentSetMethods`].)
615    ///
616    /// ### Top-level attributes:
617    ///
618    ///  * **`#[deftly(netdoc(no_extra_args))]**:
619    ///
620    ///    Reject, rather than ignore, additional arguments found in the document
621    ///    which aren't described by the struct.
622    ///
623    /// ### Field-level attributes:
624    ///
625    ///  * **`#[deftly(netdoc(object))]**:
626    ///
627    ///    The field is the Object.
628    ///    It must implement [`ItemObjectParseable`]
629    ///    (so it can be be `Option<impl ItemObjectParseable>`
630    ///    for an optional item.)
631    ///
632    ///    Only allowed once.
633    ///    If omittted, any object is rejected.
634    ///
635    ///  * **`#[deftly(netdoc(sig_hash = "HASH_METHOD"))]**:
636    ///
637    ///    This item is a signature item.
638    ///    [`SignatureItemParseable`] will be implemented instead of [`ItemValueParseable`].
639    ///
640    ///    This field is a document hash.
641    ///    The hash will be computed using `HASH_METHOD`,
642    ///    which will be resolved with `sig_hash_methods::*` in scope.
643    ///
644    ///    `fn HASH_METHOD(body: &SignatureHashInputs) -> HASH_FIELD_VALUE`.
645    export ItemValueParseable for struct, expect items:
646
647    ${define P { $crate::parse2::internal_prelude }}
648
649    ${defcond F_REST fmeta(netdoc(rest))}
650    ${defcond F_OBJECT fmeta(netdoc(object))}
651    ${defcond F_SIG_HASH fmeta(netdoc(sig_hash))}
652    ${defcond F_NORMAL not(any(F_REST, F_OBJECT, F_SIG_HASH))}
653
654    ${defcond T_IS_SIGNATURE not(approx_equal(${for fields { ${when F_SIG_HASH} 1 }}, {}))}
655    ${define TRAIT ${if T_IS_SIGNATURE { SignatureItemParseable } else { ItemValueParseable }}}
656    ${define METHOD ${if T_IS_SIGNATURE { from_unparsed_and_body } else { from_unparsed }}}
657
658    impl<$tgens> $P::$TRAIT for $ttype {
659        fn $METHOD<'s>(
660            mut input: $P::UnparsedItem<'s>,
661          ${if T_IS_SIGNATURE {
662            document_body: &SignatureHashInputs<'_>,
663          }}
664        ) -> Result<Self, $P::EP>
665        {
666            #[allow(unused_imports)] // false positive when macro is used with prelude in scope
667            use $P::*;
668
669            let object = input.object();
670            #[allow(unused)]
671            let mut args = input.args_mut();
672          $(
673            let $fpatname = ${select1
674              F_NORMAL {
675                  <$ftype as ItemArgumentParseable>::from_args(&mut args, stringify!($fname))?
676              }
677              all(F_OBJECT, not(fmeta(netdoc(object(label))))) {
678                  <$ftype as ItemObjectParseable>::from_bytes_option(
679                      object
680                          .map(|object| object.decode_data()).transpose()?
681                          .as_deref()
682                  )?
683              }
684              all(F_OBJECT, fmeta(netdoc(object(label)))) { {
685                  let object = object.ok_or_else(|| EP::MissingObject)?;
686                  if object.label() != ${fmeta(netdoc(object(label))) as str} {
687                      return Err(EP::ObjectIncorrectLabel)
688                  }
689                  object.decode_data()?
690              } }
691              F_REST {
692                  // consumes `args`, leading to compile error if the rest field
693                  // isn't last (or is combined with no_extra_args).
694                  <$ftype as FromStr>::parse(args.into_rest())?
695              }
696              F_SIG_HASH { {
697                  #[allow(unused_imports)]
698                  use $P::sig_hash_methods::*;
699                  ${fmeta(netdoc(sig_hash)) as path}(&document_body)
700              } }
701            };
702          )
703          ${if approx_equal({}, $( ${when F_OBJECT} $fname )) {
704            if object.is_some() {
705                return Err(EP::ObjectUnexpected);
706            }
707          }}
708          ${if tmeta(netdoc(no_extra_args)) {
709            args.reject_extra_args()?;
710          }}
711            Ok($tname { $( $fname: $fpatname, ) })
712        }
713    }
714}