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}