tor_netdoc/parse2/derive.rs
1//! Deriving `NetdocParseable`
2
3use super::*;
4
5//==================== Common definitions used by many of the macros ====================
6
7/// Helper to implemnet `dtrace!` inside `NetdocParseable` derive-deftly macro.
8#[doc(hidden)]
9#[allow(clippy::print_stderr)]
10pub fn netdoc_parseable_derive_debug(ttype: &str, msg: &str, vals: &[&dyn Debug]) {
11 // We use `eprintln!` so that the output is captured as expected under cargo test.
12 // We buffer the output into a string so that it a;ll appears at once,
13 // rather than possibly being interleaved with similar output for other types.
14 let mut out = String::new();
15 (|| {
16 write!(out, "netdoc {ttype} parse: {msg}")?;
17 for val in vals {
18 write!(out, ", {val:?}")?;
19 }
20 writeln!(out)
21 })()
22 .expect("write to string failed");
23
24 eprint!("{out}");
25}
26
27define_derive_deftly_module! {
28 /// Common definitions for all parsing macros.
29 NetdocParseAnyCommon beta_deftly:
30
31 // Convenience alias for our prelude
32 ${define P { $crate::parse2::internal_prelude }}
33
34 // Defines the `dtrace` macro.
35 ${define DEFINE_DTRACE {
36 #[allow(unused_macros)]
37 macro_rules! dtrace { { $$msg:literal $$(, $$val:expr )* $$(,)? } => {
38 ${if tmeta(netdoc(debug)) {
39 $P::netdoc_parseable_derive_debug(
40 ${concat $ttype},
41 $$msg,
42 &[ $$( &&$$val as _, )* ],
43 )
44 }}
45 }}
46 }}
47}
48
49define_derive_deftly_module! {
50 /// Common definitions for `NetdocParseable`, `NetdocParseableFields`,
51 /// and `NetdocParseableSignatures`
52 ///
53 /// The including macro is expected to define:
54 ///
55 /// * **`THIS_ITEM`**: consumes the next item and evaluates to it as an `UnparsedItem`.
56 /// See the definition in `NetdocParseable`.
57 ///
58 /// * **`F_ACCUMULATE_VAR`** the variable or field into which to accumulate
59 /// normal items for this field. Must be of type `&mut $F_ACCUMULATE_TYPE`.
60 ///
61 /// Importer must also import `NetdocSomeItemsDeriveCommon` and `NetdocDeriveAnyCommon`.
62 NetdocSomeItemsParseableCommon beta_deftly:
63
64 // The effective field type for parsing.
65 //
66 // Handles #[deftly(netdoc(default))], in which case we parse as if the field was Option,
67 // and substitute in the default at the end.
68 //
69 ${define F_EFFECTIVE_TYPE {
70 ${if all(fmeta(netdoc(default))) {
71 Option::<$ftype>
72 } else {
73 $ftype
74 }}
75 }}
76
77 // Provide `$<selector_ $fname>` for every (suitable) field.
78 ${define ITEM_SET_SELECTORS {
79 $(
80 ${when not(any(F_FLATTEN, F_SKIP))}
81
82 // See `mod multiplicity`.
83 ${if not(all(F_INTRO, fmeta(netdoc(with)))) {
84 // If the intro it has `with`, we don't check its trait impl, and this ends up unused
85 let $<selector_ $fname> = $F_SELECTOR_VALUE;
86 }}
87 )
88 }}
89 // The item set selector for this field.
90 // We must provide this, rather than expanding $<selector_ $fname> at the use sites,
91 // because the identifier `selector_` has different macro_rules hygiene here vs there!
92 // TODO derive-deftly#130
93 ${define F_SELECTOR $<selector_ $fname>}
94 // The selector value for this field. Used where we don't want to bind a selector
95 // for every field with $ITEM_SET_SELECTORS (and within $ITEM_SET_SELECTORS).
96 ${define F_SELECTOR_VALUE {( MultiplicitySelector::<$F_EFFECTIVE_TYPE>::default() )}}
97 // Check that every field type implements the necessary trait.
98 ${define CHECK_FIELD_TYPES_PARSEABLE {
99 $(
100 ${when not(any(F_FLATTEN, F_SKIP))}
101
102 // Expands to `selector_FIELD.check_SOMETHING();`
103 //
104 // If the relevant trait isn't implemented, rustc reports the error by
105 // pointing at the `check-something` call. We re-span that identifier
106 // to point to the field name, so that's where the error is reported.
107 //
108 // Without this, we just get a report that `item` doesn't implement the required
109 // trait - but `item` is a local variable here, so the error points into the macro
110 ${if not(all(any(F_INTRO, F_NORMAL), fmeta(netdoc(with)))) {
111 $<selector_ $fname> . ${paste_spanned $fname ${select1
112 any(F_INTRO, F_NORMAL){
113 // For the intro item, this is not completely precise, because the
114 // it will allow Option<> and Vec<> which aren't allowed there.
115 ${if
116 fmeta(netdoc(single_arg)) { check_item_argument_parseable }
117 else { check_item_value_parseable }
118 }
119 }
120 F_SIGNATURE { check_signature_item_parseable }
121 F_SUBDOC { check_subdoc_parseable }
122 }} (
123 ${if F_SIGNATURE { sig_hashes, }}
124 );
125 }}
126 )
127 }}
128
129 // Convert the UnparsedItem (in `item` to the value (to accumulate).
130 // Expands to an expression.
131 ${define ITEM_VALUE_FROM_UNPARSED {
132 ${if fmeta(netdoc(with)) {
133 ${fmeta(netdoc(with)) as path}
134 ::${paste_spanned $fname from_unparsed}
135 (item)?
136 } else if fmeta(netdoc(single_arg)) { {
137 let item = ItemValueParseable::from_unparsed(item)?;
138 let (item,) = item;
139 item
140 } } else {
141 ItemValueParseable::from_unparsed(item)?
142 }}
143 }}
144
145 // Type into which we accumulate value(s) of this field
146 ${define F_ACCUMULATE_TYPE {
147 ${if F_FLATTEN {
148 <$ftype as $P::NetdocParseableFields>::Accumulator
149 } else {
150 Option::<$F_EFFECTIVE_TYPE>
151 }
152 }}}
153
154 // Parse the intro item and bind `$fpatname` accumulator for each field.
155 //
156 // For the intro item, parse it and bind it to $fpatname.
157 //
158 // For other items, set up a mutable $fpatname, initialised to `default()` (normally None).
159 ${define INIT_ACCUMULATE_VARS {
160 $( ${select1 F_INTRO {
161
162 let item = input.next_item()?.ok_or(EP::EmptyDocument)?;
163 dtrace!("intro", item);
164 if !Self::is_intro_item_keyword(item.keyword()) {
165 Err(EP::WrongDocumentType)?;
166 }
167 let $fpatname: $ftype = $ITEM_VALUE_FROM_UNPARSED;
168
169 } F_SKIP {
170
171 } else {
172
173 let mut $fpatname = $F_ACCUMULATE_TYPE::default();
174
175 }})
176 }}
177
178 // Accumulates `item` (which must be `ItemSetMethods::Each`) into `$F_ACCUMULATE_VAR`
179 ${define ACCUMULATE_ITEM_VALUE { {
180 dtrace!($"accumulating $fname", $F_SELECTOR.item_set_debug());
181 $F_SELECTOR.${paste_spanned $fname accumulate}($F_ACCUMULATE_VAR, item)?;
182 } }}
183
184 // Handle a nonstructural field, parsing and accumulating its value
185 //
186 // Looks at `kw` for the keyword.
187 //
188 // Expands to a series of `if ... { ... } else`.
189 // The use site must provide (maybe further arms) and a fallback block!
190 //
191 // If the item is the intro item for this document, evaluates `break` -
192 // so if `f_INTRO` is not trivially false, must be expanded within a field loop.
193 ${define NONSTRUCTURAL_ACCUMULATE_ELSE {
194 ${for fields {
195 ${when not(any(F_FLATTEN, F_SUBDOC, F_SKIP))}
196
197 if kw == $F_KEYWORD {
198 ${select1
199 F_NORMAL {
200 let item = $THIS_ITEM;
201 dtrace!("is normal", item);
202 let item = $ITEM_VALUE_FROM_UNPARSED;
203 $ACCUMULATE_ITEM_VALUE
204 }
205 F_SIGNATURE {
206 let hash_inputs = input
207 .peek_signature_hash_inputs(signed_doc_body)?
208 .expect("not eof, we peeked kw");
209
210 let item = $THIS_ITEM;
211 dtrace!("is signature", item);
212 let item = SignatureItemParseable::from_unparsed_and_body(
213 item,
214 &hash_inputs,
215 AsMut::as_mut(sig_hashes),
216 )?;
217 $ACCUMULATE_ITEM_VALUE
218 }
219 F_INTRO {
220 dtrace!("is intro", kw);
221 break;
222 } // start of next similar document
223 }
224 } else
225 }}
226 ${for fields {
227 ${when F_FLATTEN}
228
229 if $ftype::is_item_keyword(kw) {
230 dtrace!(${concat "is flatten in " $fname}, kw);
231 let item = $THIS_ITEM;
232 <$ftype as NetdocParseableFields>::accumulate_item($F_ACCUMULATE_VAR, item)?;
233 } else
234 }}
235 }}
236
237 // Completes a document
238 //
239 // The fields accumulated so far must be in `$fpatname` (as a value, not a ref,
240 // and therefore not in $F_ACCUMULATE_VAR).
241 //
242 // Expands to code which resolves the fields, and ends with `Ok(document value)`.
243 ${define FINISH_RESOLVE {
244 ${for fields {
245 ${select1
246 F_INTRO {}
247 any(F_NORMAL, F_SIGNATURE) {
248 let $fpatname = $F_SELECTOR.finish($fpatname, $F_KEYWORD_REPORT)?;
249 }
250 F_FLATTEN {
251 let $fpatname = <$ftype as NetdocParseableFields>::finish($fpatname)?;
252 }
253 F_SUBDOC {
254 let $fpatname = $F_SELECTOR.finish_subdoc($fpatname)?;
255 }
256 F_SKIP {
257 #[allow(non_snake_case)]
258 let $fpatname = Default::default();
259 }
260 }
261 }}
262 $(
263 ${when not(any(F_INTRO, F_SKIP))}
264 // These conditions are mirrored in NetdocSomeItemsEncodableCommon,
265 // which is supposed to recognise netdoc(default) precisely when we do.
266 ${if fmeta(netdoc(default)) {
267 let $fpatname = Option::unwrap_or_default($fpatname);
268 }}
269 )
270 Ok($vpat)
271 }}
272}
273
274//==================== Main whole document parsing impl ====================
275//
276// deftly module ` NetdocParseable`:
277//
278// * IMPL_NETDOC_PARSEABLE expanding to `impl NetdocParseable { ... }`
279//
280// Much of the heavy lifting is done in the NetdocSomeItemsParseableCommon deftly module.
281
282define_derive_deftly_module! {
283 /// Provides `IMPL_NETDOC_PARSEABLE` which impls `NetdocParseable`
284 ///
285 /// Used by the `NetdocParseable` and `NetdocParseableUnverified` derives.
286 NetdocParseable beta_deftly:
287
288 use NetdocDeriveAnyCommon;
289 use NetdocParseAnyCommon;
290 use NetdocEntireDeriveCommon;
291 use NetdocSomeItemsDeriveCommon;
292 use NetdocSomeItemsParseableCommon;
293
294 ${define F_ACCUMULATE_VAR { (&mut $fpatname) }}
295
296 ${define IMPL_NETDOC_PARSEABLE {
297 impl<$tgens> $P::NetdocParseable for $NETDOC_PARSEABLE_TTYPE {
298 fn doctype_for_error() -> &'static str {
299 ${tmeta(netdoc(doctype_for_error)) as expr,
300 default ${concat ${for fields { ${when F_INTRO} $F_KEYWORD_STR }}}}
301 }
302
303 fn is_intro_item_keyword(kw: $P::KeywordRef<'_>) -> bool {
304 use $P::*;
305
306 ${for fields {
307 ${when F_INTRO}
308 ${loop_exactly_1 "internal error, somehow not exactly one intro item!"}
309 kw == $F_KEYWORD
310 }}
311 }
312
313 fn is_structural_keyword(kw: $P::KeywordRef<'_>) -> Option<$P::IsStructural> {
314 #[allow(unused_imports)] // not used if there are no subdocs
315 use $P::*;
316
317 if Self::is_intro_item_keyword(kw) {
318 return Some(IsStructural)
319 }
320
321 ${for fields {
322 ${when F_SUBDOC}
323 if let y @ Some(_) = $F_SELECTOR_VALUE.is_structural_keyword(kw) {
324 return y;
325 }
326 }}
327
328 None
329 }
330
331 //##### main parsing function #####
332
333 #[allow(clippy::redundant_locals)] // let item = $THIS_ITEM, which might be item
334 fn from_items<'s>(
335 input: &mut $P::ItemStream<'s>,
336 outer_stop: $P::stop_at!(),
337 ) -> $P::Result<Self, $P::ErrorProblem> {
338 use $P::*;
339 $DEFINE_DTRACE
340 $FIELD_ORDERING_CHECK
341
342 //----- prepare item set selectors for every field -----
343 $ITEM_SET_SELECTORS
344 $CHECK_FIELD_TYPES_PARSEABLE
345
346 // Is this an intro item keyword ?
347 //
348 // Expands to an appropriate `is_intro_item_keyword` method invocation,
349 // but *without arguments*. So, something a bit like an expression of type
350 // fn(KeywordRef) -> bool
351 ${define F_SUBDOC_IS_INTRO_ITEM_KEYWORD {
352 ${if not(F_SUBDOC) { ${error "internal-error: subdoc kw, but not subdoc field"} }}
353 $F_SELECTOR.is_intro_item_keyword
354 }}
355
356 //----- Helper fragments for parsing individual pieces of the document -----
357
358 // Peeks a keyword, and returns it but only if it's part of this (sub)doc.
359 // Return `None` if it was in outer_stop
360 let peek_keyword = |input: &mut ItemStream<'s>| -> Result<Option<KeywordRef<'s>>, EP> {
361 let Some(kw) = input.peek_keyword()? else {
362 dtrace!("stopping, because EOF");
363 return Ok(None)
364 };
365 if outer_stop.stop_at(kw) {
366 dtrace!("stopping, because peeked", kw);
367 return Ok(None)
368 }
369 Ok(Some(kw))
370 };
371
372 // Returns the actual item as an UnparsedItem, committing to consuming it.
373 // Can panic if called without previous `peek_keyword`.
374 ${define THIS_ITEM {
375 input.next_item()?.expect("peeked")
376 }}
377
378 //----- keyword classification closures -----
379
380 // Is this a keyword for one of our sub-documents?
381 let is_subdoc_kw = ${for fields {
382 ${when F_SUBDOC}
383 StopAt(|kw: KeywordRef<'_>| $F_SUBDOC_IS_INTRO_ITEM_KEYWORD(kw)) |
384 }}
385 StopAt(false)
386 ;
387 // Is this a keyword for one of our parents or sub-documents?
388 let inner_stop = outer_stop | is_subdoc_kw;
389
390 //========== actual parsing ==========
391
392 // For each parsing loop/section, where we aren't looking for precisely one thing,
393 // we should explicitly decide what to do with each of:
394 // - F_INTRO - intro item for this document (maybe next instance in parent)
395 // - F_NORMAL - normal items
396 // - subdocuments, is_subdoc_kw and F_SUBDOC
397 // - our parent's structural keywords, outer_stop
398 // (this includes signature items for the signed version of this doc)
399 // 5 cases in all.
400
401 //----- Parse the intro item, and introduce bindings for the other items. -----
402
403 dtrace!("looking for intro item");
404 $INIT_ACCUMULATE_VARS
405
406 //----- Parse the normal items -----
407 dtrace!("looking for normal items");
408
409 while let Some(kw) = peek_keyword(input)? {
410 dtrace!("for normal, peeked", kw);
411 if inner_stop.stop_at(kw) {
412 dtrace!("is inner stop", kw);
413 break;
414 };
415
416 $NONSTRUCTURAL_ACCUMULATE_ELSE
417 {
418 dtrace!("is unknown (in normal)");
419 let _: UnparsedItem = $THIS_ITEM;
420 }
421 }
422
423 //----- Parse the subdocs, in order -----
424 dtrace!("looking for subdocs");
425
426 ${for fields {
427 ${when F_SUBDOC}
428 dtrace!("looking for subdoc", $F_KEYWORD_REPORT);
429
430 loop {
431 let Some(kw) = peek_keyword(input)? else { break };
432 dtrace!("for subdoc, peek", kw);
433
434 if !$F_SUBDOC_IS_INTRO_ITEM_KEYWORD(kw) {
435 dtrace!("is not this subdoc", kw);
436 break;
437 };
438
439 $F_SELECTOR.can_accumulate(&mut $fpatname)?;
440
441 dtrace!("is this subdoc", kw);
442 let item = NetdocParseable::from_items(input, inner_stop);
443 dtrace!("parsed this subdoc", item.as_ref().map(|_| ()));
444 let item = item?;
445
446 $ACCUMULATE_ITEM_VALUE
447 }
448 }}
449
450 // Resolve all the fields
451 dtrace!("reached end, resolving");
452
453 $FINISH_RESOLVE_PARSEABLE
454 }
455 }
456 }}
457}
458
459//==================== NetdocParseable user-facing derive macro ====================
460//
461// deftly template `NetdocParseable`:
462//
463// * main entrypoint for deriving the `NetdocParseable` trait
464// * docs for the meta attributes we support during document parsing
465//
466// The actual implementation is in the `NetdocParseable` deftly module, above.
467
468define_derive_deftly! {
469 use NetdocParseable;
470
471 /// Derive [`NetdocParseable`] for a document (or sub-document)
472 ///
473 // NB there is very similar wording in the NetdocEncodable derive docs.
474 // If editing any of this derive's documentation, considering editing that too.
475 //
476 /// ### Expected input structure
477 ///
478 /// Should be applied named-field struct, where each field is
479 /// an Item which may appear in the document,
480 /// or a sub-document.
481 ///
482 /// The first field will be the document's intro Item.
483 /// The expected Keyword for each Item will be kebab-case of the field name.
484 ///
485 /// ### Field type
486 ///
487 /// Each field must be
488 /// * `impl `[`ItemValueParseable`] for an "exactly once" field,
489 /// * `Vec<T: ItemValueParseable>` for "zero or more", or
490 /// * `BTreeSet<T: ItemValueParseable + Ord>`, or
491 /// * `Option<T: ItemValueParseable>` for "zero or one".
492 ///
493 /// We don't directly support "at least once":
494 /// the parsed network document doesn't imply the invariant
495 /// that at least one such item was present.
496 // We could invent a `NonemptyVec` or something for this.
497 ///
498 /// (This is implemented via types in the [`multiplicity`] module,
499 /// specifically [`ItemSetSelector`].)
500 ///
501 /// ### Signed documents
502 ///
503 /// To handle signed documents define two structures:
504 ///
505 /// * `Foo`, containing only the content, not the signatures.
506 /// Derive [`NetdocParseableUnverified`](derive_deftly_template_NetdocUnverified).
507 /// * `FooSignatures`, containing only the signatures.
508 /// Derive `NetdocParseableSignatures`.
509 ///
510 /// Don't mix signature items with non-signature items in the same struct.
511 /// (This wouldn't compile, because the field type would implement the wrong trait.)
512 ///
513 /// ### Top-level attributes:
514 ///
515 /// * **`#[deftly(netdoc(doctype_for_error = EXPRESSION))]`**:
516 ///
517 /// Specifies the value to be returned from
518 /// [`NetdocParseable::doctype_for_error`].
519 ///
520 /// Note, must be an expression, so for a literal, nested `""` are needed.
521 ///
522 /// The default is the intro item keyword.
523 ///
524 /// * **`#[deftly(netdoc(debug))]`**:
525 ///
526 /// The generated implementation will generate copious debug output
527 /// to the program's stderr when it is run.
528 /// Do not enable in production!
529 ///
530 /// ### Field-level attributes:
531 ///
532 /// * **`#[deftly(netdoc(keyword = STR))]`**:
533 ///
534 /// Use `STR` as the Keyword for this Item.
535 ///
536 /// * **`#[deftly(netdoc(single_arg))]`**:
537 ///
538 /// The field type implements `ItemArgumentParseable`,
539 /// instead of `ItemValueParseable`,
540 /// and is parsed as if `(FIELD_TYPE,)` had been written.
541 ///
542 /// * **`#[deftly(netdoc(with = MODULE))]`**:
543 ///
544 /// Instead of `ItemValueParseable`, the item is parsed with `MODULE::from_unparsed`,
545 /// which must have the same signature as [`ItemValueParseable::from_unparsed`].
546 ///
547 /// (Not supported for sub-documents, signature items, or field collections.)
548 ///
549 /// * **`#[deftly(netdoc(default))]`**:
550 ///
551 /// This field is optional ("at most once");
552 /// if not present, `FIELD_TYPE::default()` will be used.
553 ///
554 /// This is an alternative to declaring the field type as `Option`
555 /// With `netdoc(default)`, the field value doesn't need unwrapping.
556 /// With `Option` it is possible to see if the field was provided.
557 ///
558 /// * **`#[deftly(netdoc(flatten))]`**:
559 ///
560 /// This field is a struct containing further individual normal fields.
561 /// The Items for those individual fields can appear in *this*
562 /// outer document in any order, interspersed with other normal fields.
563 ///
564 /// The field type must implement [`NetdocParseableFields`].
565 ///
566 /// * **`#[deftly(netdoc(skip))]`**:
567 ///
568 /// This field doesn't really appear in the network document.
569 /// It won't be recognised during parsing.
570 /// Instead, `Default::default()` will be used for the field value.
571 ///
572 /// * **`#[deftly(netdoc(subdoc))]`**:
573 ///
574 /// This field is a sub-document.
575 /// The value type `T` must implement [`NetdocParseable`]
576 /// *instead of* `ItemValueParseable`.
577 ///
578 /// The field name is not used for parsging;
579 /// the sub-document's intro keyword is used instead.
580 ///
581 /// Sub-documents are expected to appear after all normal items,
582 /// in the order presented in the struct definition.
583 ///
584 /// # Example
585 ///
586 /// ```
587 /// use derive_deftly::Deftly;
588 /// use tor_netdoc::derive_deftly_template_AsMutSelf;
589 /// use tor_netdoc::derive_deftly_template_NetdocParseableSignatures;
590 /// use tor_netdoc::derive_deftly_template_NetdocParseableUnverified;
591 /// use tor_netdoc::derive_deftly_template_ItemValueParseable;
592 /// use tor_netdoc::parse2::{
593 /// parse_netdoc, ErrorProblem, ParseInput, VerifyFailed,
594 /// SignatureItemParseable, SignatureHashesAccumulator, SignatureHashInputs,
595 /// };
596 ///
597 /// #[derive(Deftly, Debug, Clone)]
598 /// #[derive_deftly(NetdocParseableUnverified)]
599 /// pub struct NdThing {
600 /// pub thing_start: (),
601 /// pub value: (String,),
602 /// }
603 ///
604 /// #[derive(Deftly, Debug, Clone)]
605 /// #[derive_deftly(NetdocParseableSignatures)]
606 /// #[deftly(netdoc(signatures(hashes_accu = UseLengthAsFoolishHash)))]
607 /// pub struct NdThingSignatures {
608 /// pub signature: FoolishSignature,
609 /// }
610 ///
611 /// #[derive(Deftly, Debug, Clone)]
612 /// #[derive_deftly(ItemValueParseable)]
613 /// #[deftly(netdoc(signature(hash_accu = UseLengthAsFoolishHash)))]
614 /// pub struct FoolishSignature {
615 /// pub doc_len: usize,
616 /// }
617 ///
618 /// #[derive(Deftly, Debug, Default, Clone)]
619 /// #[derive_deftly(AsMutSelf)]
620 /// pub struct UseLengthAsFoolishHash {
621 /// pub doc_len_actual_pretending_to_be_hash: Option<usize>,
622 /// }
623 /// impl SignatureHashesAccumulator for UseLengthAsFoolishHash {
624 /// fn update_from_netdoc_body(
625 /// &mut self,
626 /// document_body: &SignatureHashInputs<'_>,
627 /// ) -> Result<(), ErrorProblem> {
628 /// self
629 /// .doc_len_actual_pretending_to_be_hash
630 /// .get_or_insert_with(|| document_body.body().body().len());
631 /// Ok(())
632 /// }
633 /// }
634 ///
635 /// let doc_text =
636 /// r#"thing-start
637 /// value something
638 /// signature 28
639 /// "#;
640 ///
641 /// impl NdThingUnverified {
642 /// pub fn verify_foolish_timeless(self) -> Result<NdThing, VerifyFailed> {
643 /// let sig = &self.sigs.sigs.signature;
644 /// let hash = self.sigs.hashes.doc_len_actual_pretending_to_be_hash
645 /// .as_ref().ok_or(VerifyFailed::Bug)?;
646 /// if sig.doc_len != *hash {
647 /// return Err(VerifyFailed::VerifyFailed);
648 /// }
649 /// Ok(self.body)
650 /// }
651 /// }
652 ///
653 /// let input = ParseInput::new(&doc_text, "<input>");
654 /// let doc: NdThingUnverified = parse_netdoc(&input).unwrap();
655 /// let doc = doc.verify_foolish_timeless().unwrap();
656 /// assert_eq!(doc.value.0, "something");
657 /// ```
658 export NetdocParseable for struct, meta_quoted rigorous, expect items, beta_deftly:
659
660 ${define NETDOC_PARSEABLE_TTYPE { $ttype }}
661 ${define FINISH_RESOLVE_PARSEABLE $FINISH_RESOLVE}
662
663 $IMPL_NETDOC_PARSEABLE
664}
665
666//==================== NetdocParseableSignatures user-facing derive macro ====================
667//
668// deftly template `NetdocParseableSignatures`:
669//
670// * entrypoint for deriving the `NetdocParseableSignatures` trait
671// * docs for the signatures-section-specific attributes
672// * implementation of that derive
673//
674// Much of the heavy lifting is done in the NetdocSomeItemsParseableCommon deftly module.
675
676define_derive_deftly! {
677 use NetdocDeriveAnyCommon;
678 use NetdocParseAnyCommon;
679 use NetdocSomeItemsDeriveCommon;
680 use NetdocSomeItemsParseableCommon;
681
682 /// Derive [`NetdocParseable`] for the signatures section of a network document
683 ///
684 /// This type is the signatures section of another document.
685 /// Signature sections have no separate intro keyword:
686 /// every field is structural and they are recognised in any order.
687 ///
688 /// This signatures sub-document will typically be included in a
689 /// `FooUnverified` struct derived with
690 /// [`NetdocUnverified`](derive_deftly_template_NetdocUnverified),
691 /// rather than included anywhere manually.
692 ///
693 /// ### Expected input structure
694 ///
695 /// Should be applied named-field struct, where each field
696 /// implements [`SignatureItemParseable`],
697 /// or is a `SignatureItemParseable` in `Vec` or `BTreeSet` or `Option`.
698 ///
699 /// ### Attributes
700 ///
701 /// * The following top-level attributes are supported:
702 /// `#[deftly(netdoc(debug))]`
703 ///
704 /// * The following field-level attributes are supported:
705 /// `#[deftly(netdoc(keyword = STR))]`
706 /// `#[deftly(netdoc(default))]`
707 /// `#[deftly(netdoc(single_arg))]`
708 /// `#[deftly(netdoc(with = MODULE))]`
709 /// `#[deftly(netdoc(flatten))]`
710 /// `#[deftly(netdoc(skip))]`
711 ///
712 /// ### Signature item ordering, and signatures covering signatures
713 ///
714 /// The derived code does not impose any mutual ordering of signatures.
715 /// If signatures are independent, hashing can be done with [`SignedDocumentBody`]
716 /// (from [`SignatureHashInputs::body`]).
717 ///
718 /// In sane netdoc signature scheme, no signatures would cover other signatures,
719 /// and there would be no ordering requirement on signatures on the same document.
720 /// A relying party would verify the signatures that they are proposing to rely on
721 /// (which would generally include signatures for *one* algorithm, not several)
722 /// and ignore the others.
723 ///
724 /// (Such a signature, which also does not include any of its own item encoding
725 /// in its hash, is called Orderly. See [SignedDocumentBody].)
726 ///
727 /// Unfortunately, many Tor netdocs have signature schemes
728 /// which are not sane (by this definition).
729 ///
730 /// When signatures are specified to cover other signatures,
731 /// the signature item implementation must contain ad-hoc code in
732 /// [`SignatureItemParseable::from_unparsed_and_body`].
733 /// to hash not only the body, but also the prior signatures.
734 /// Methods on [`SignatureHashInputs`] are available to get
735 /// the relevant parts of the input document text
736 /// (eg, [`document_sofar`](SignatureHashInputs::document_sofar)).
737 ///
738 /// When the spec states a required ordering on signature items,
739 /// this should be enforced by ad-hoc code in implementation(s) of
740 /// `SignatureItemParseable`.
741 /// The implementation should use
742 /// [`HashAccu`](SignatureItemParseable::HashAccu)
743 /// to store any necessary state.
744 /// Usually, this can be achieved by using the same Rust struct for the
745 /// `HashAccu` of each of the signature items:
746 /// that will make the signature hashes computed so far, for items seen so far,
747 /// visible to subsequent items;
748 /// the subsequent items can check that the prior items filled in the hash,
749 /// thus imposing an ordering.
750 ///
751 /// Alternatively, the ordering could be enforced in the user-supplied
752 /// ad-hoc `verify` function(s) on `FooUUnverified`.
753 ///
754 /// Note that this enforcement should be done for protocol compliance
755 /// and availability reasons, but is not a security issue.
756 /// There is not a security risk from accepting documents some of whose signatures
757 /// aren't covered by other signatures even though the protocol specifies they should be:
758 /// relying parties *verify* the signatures but do not treat them as trusted data.
759 /// So there is no engineered safeguard against failing to implement
760 /// signature item ordering checks.
761 export NetdocParseableSignatures for struct, meta_quoted rigorous, expect items, beta_deftly:
762
763 ${defcond F_INTRO false}
764 ${defcond F_SUBDOC false}
765 ${defcond F_SIGNATURE true}
766
767 // NetdocParseableSignatures::HashesAccu
768 ${define SIGS_HASHES_ACCU_TYPE { ${tmeta(netdoc(signatures(hashes_accu))) as ty} }}
769
770 ${define THIS_ITEM { input.next_item()?.expect("peeked") }}
771 ${define F_ACCUMULATE_VAR { (&mut $fpatname) }}
772
773 impl<$tgens> $P::NetdocParseableSignatures for $ttype {
774 type HashesAccu = $SIGS_HASHES_ACCU_TYPE;
775
776 fn is_item_keyword(kw: $P::KeywordRef<'_>) -> bool {
777 use $P::*;
778 ${for fields {
779 kw == $F_KEYWORD ||
780 }}
781 false
782 }
783
784 #[allow(clippy::redundant_locals)] // let item = $THIS_ITEM, which might be item
785 fn from_items<'s>(
786 input: &mut $P::ItemStream<'s>,
787 signed_doc_body: $P::SignedDocumentBody<'s>,
788 sig_hashes: &mut $SIGS_HASHES_ACCU_TYPE,
789 outer_stop: $P::stop_at!(),
790 ) -> $P::Result<$ttype, $P::ErrorProblem> {
791 use $P::*;
792 $DEFINE_DTRACE
793
794 //----- prepare item set selectors for every field -----
795 $ITEM_SET_SELECTORS
796 $CHECK_FIELD_TYPES_PARSEABLE
797 $INIT_ACCUMULATE_VARS
798
799 //----- parse the items -----
800 dtrace!("looking for signature items");
801
802 while let Some(kw) = input.peek_keyword()? {
803 dtrace!("for signatures, peeked", kw);
804 if outer_stop.stop_at(kw) {
805 dtrace!("is outer stop", kw);
806 break;
807 };
808
809 $NONSTRUCTURAL_ACCUMULATE_ELSE
810 {
811 dtrace!("is unknown (in signatures)");
812 let _: UnparsedItem = $THIS_ITEM;
813 }
814 }
815
816 // Resolve all the fields
817 dtrace!("reached end, resolving");
818
819 $FINISH_RESOLVE
820 }
821 }
822}
823
824//==================== NetdocParseableFields user-facing derive macro ====================
825//
826// deftly template `NetdocParseableFields`
827//
828// * entrypoint for deriving the `NetdocParseableFields` trait
829// * docs and implementation for that derive
830//
831// Much of the heavy lifting is done in the NetdocSomeItemsParseableCommon deftly module.
832
833define_derive_deftly! {
834 use NetdocDeriveAnyCommon;
835 use NetdocParseAnyCommon;
836 use NetdocFieldsDeriveCommon;
837 use NetdocSomeItemsDeriveCommon;
838 use NetdocSomeItemsParseableCommon;
839
840 /// Derive [`NetdocParseableFields`] for a struct with individual items
841 ///
842 /// Defines a struct `FooNetdocParseAccumulator` to be the
843 /// `NetdocParseableFields::Accumulator`.
844 ///
845 /// Similar to
846 /// [`#[derive_deftly(NetdocParseable)]`](derive_deftly_template_NetdocParseable),
847 /// but:
848 ///
849 /// * Derives [`NetdocParseableFields`]
850 $DOC_NETDOC_FIELDS_DERIVE_SUPPORTED
851 ///
852 export NetdocParseableFields for struct , meta_quoted rigorous, expect items, beta_deftly:
853
854 ${define THIS_ITEM item}
855 ${define F_ACCUMULATE_VAR { (&mut acc.$fname) }}
856
857 #[doc = ${concat "Partially parsed `" $tname "`"}]
858 ///
859 /// Used for [`${concat $P::NetdocParseableFields::Accumulator}`].
860 #[derive(Default, Debug)]
861 $tvis struct $<$tname NetdocParseAccumulator><$tdefgens> { $(
862 $fname: $F_ACCUMULATE_TYPE,
863 ) }
864
865 impl<$tgens> $P::NetdocParseableFields for $ttype {
866 type Accumulator = $<$ttype NetdocParseAccumulator>;
867
868 fn is_item_keyword(
869 #[allow(unused_variables)] // If there are no fields, this is unused
870 kw: $P::KeywordRef<'_>,
871 ) -> bool {
872 #[allow(unused_imports)] // false positives in some situations
873 use $P::*;
874
875 ${for fields {
876 ${when not(F_FLATTEN)}
877 ${when not(F_SKIP)}
878 kw == $F_KEYWORD ||
879 }}
880 ${for fields {
881 ${when F_FLATTEN}
882 ${when not(F_SKIP)}
883 <$ftype as NetdocParseableFields>::is_item_keyword(kw) ||
884 }}
885 false
886 }
887
888 #[allow(clippy::redundant_locals)] // let item = $THIS_ITEM, which might be item
889 fn accumulate_item(
890 #[allow(unused_variables)] // If there are no fields, this is unused
891 acc: &mut Self::Accumulator,
892 #[allow(unused_variables)] // If there are no fields, this is unused
893 item: $P::UnparsedItem<'_>,
894 ) -> $P::Result<(), $P::ErrorProblem> {
895 #[allow(unused_imports)] // false positives in some situations
896 use $P::*;
897 $DEFINE_DTRACE
898
899 $ITEM_SET_SELECTORS
900 $CHECK_FIELD_TYPES_PARSEABLE
901
902 #[allow(unused_variables)] // If there are no fields, this is unused
903 let kw = item.keyword();
904
905 $NONSTRUCTURAL_ACCUMULATE_ELSE
906 {
907 panic!("accumulate_item called though is_item_keyword returns false");
908 }
909
910 #[allow(unreachable_code)] // If there are no fields!
911 Ok(())
912 }
913
914 fn finish(
915 #[allow(unused_variables)] // If there are no fields, this is unused
916 acc: Self::Accumulator
917 ) -> $P::Result<Self, $P::ErrorProblem> {
918 #[allow(unused_imports)] // false positives in some situations
919 use $P::*;
920 $DEFINE_DTRACE
921
922 dtrace!("finish, resolving");
923
924 $ITEM_SET_SELECTORS
925
926 $(
927 ${when not(F_SKIP)}
928 let $fpatname = acc.$fname;
929 )
930 $FINISH_RESOLVE
931 }
932 }
933}
934
935//==================== NetdocParseableUnverified user-facing derive macro ====================
936//
937// deftly template `NetdocParseableUnverified`
938//
939// * entrypoint for deriving the `FooUnverified` struct implementing `NetdocParseable`
940// (and supporting items such as `FooUnverifiedParsedBody` structs and its impl).
941// * docs for that derive, including doc-level signatures-related attributes
942// * implementation glue for those derived impls
943//
944// The principal derived parsing impl on the body type `Foo` is expanded by this macro,
945// but that is implemented via IMPL_NETDOC_PARSEABLE in the NetdocParseable deftly module.
946//
947// The substantive code to implement `NetdocParseable` for `FooUnverified` is
948// in the `ItemStream::parse_signed` helper function; a call to that is expanded here.
949
950define_derive_deftly! {
951 use NetdocParseable;
952
953 /// Derive `NetdocParseable` for a top-level signed document
954 ///
955 /// ### Expected input structure
956 ///
957 /// Apply this derive to the main body struct `Foo`,
958 /// which should meet all the requirements to derive
959 /// [`NetdocParseable`](derive_deftly_template_NetdocParseable).
960 ///
961 /// Usually, the caller will provide suitable ad-hoc `.verify_...` methods
962 /// on `FooUnverified`.
963 ///
964 /// ### Generated code
965 ///
966 /// Supposing your input structure is `Foo`, this macro will
967 /// generate a `**struct FooUnverified`**
968 /// implementing [`NetdocParseable`] and [`NetdocUnverified`]:
969 ///
970 /// ```rust,ignore
971 /// # struct Foo; struct FooSignatures;
972 /// pub struct FooUnverified {
973 /// body: Foo,
974 /// pub sigs: SignaturesData<FooUnverified>,
975 /// }
976 /// ```
977 ///
978 /// Also generated is `FooUnverifiedParsedBody`
979 /// and an impl of [`HasUnverifiedParsedBody`] on `Foo`.
980 /// These allow the generated code to call [`ItemStream::parse_signed`]
981 /// and it should not normally be necessary to use them elsewhere.
982 ///
983 /// ### Required top-level attributes:
984 ///
985 /// * **`#[deftly(netdoc(signature = TYPE))]`**:
986 /// Type of the signature(s) section.
987 ///
988 /// TYPE must implement `NetdocParseable`,
989 /// with `is_intro_item_keyword` reporting *every* signature keyword.
990 /// Normally this is achieved with
991 /// `#[derive_deftly(NetdocParseable)] #[deftly(netdoc(signatures))]`.
992 ///
993 /// ### Optional attributes
994 ///
995 /// All the attributes supported by the `NetdocParseable` derive are supported.
996 //
997 // We don't make NetdocUnverified a generic struct because
998 // - the defining module (crate) will want to add verification methods,
999 // which means they must define the struct
1000 // - that lets the actual `body` field be private to the defining module.
1001 export NetdocParseableUnverified for struct, meta_quoted rigorous, expect items, beta_deftly:
1002
1003 ${define NETDOC_PARSEABLE_TTYPE { $<$ttype UnverifiedParsedBody> }}
1004 ${define FINISH_RESOLVE_PARSEABLE {
1005 { $FINISH_RESOLVE }
1006 .map(|unverified| $<$tname UnverifiedParsedBody> { unverified })
1007 }}
1008
1009 $IMPL_NETDOC_PARSEABLE
1010
1011 // FooSignatures (type name)
1012 ${define SIGS_TYPE { $< ${tmeta(netdoc(signatures)) as ty, default $<$ttype Signatures>} > }}
1013 ${define SIGS_DATA_TYPE { $P::SignaturesData<$<$ttype Unverified>> }}
1014 ${define SIGS_HASHES_ACCU_TYPE { <$SIGS_TYPE as $P::NetdocParseableSignatures>::HashesAccu }}
1015
1016 $///Signed (unverified) form of [`$tname`]
1017 ///
1018 /// Embodies:
1019 ///
1020 $/// * **[`$tname`]**: document body
1021 $/// * **[`$SIGS_TYPE`]**: signatures
1022 ///
1023 /// If this type was parsed from a document text,
1024 /// the signatures have *not* yet been verified.
1025 ///
1026 /// Use a `.verify_...` method to obtain useable, verified, contents.
1027 #[derive(Debug, Clone)]
1028 $tvis struct $<$ttype Unverified> {
1029 /// The actual body
1030 //
1031 // Misuse is prevented by this field not being public.
1032 // It can be accessed only in this module, where the verification functions are.
1033 body: $ttype,
1034
1035 /// Signatures
1036 $tvis sigs: $SIGS_DATA_TYPE,
1037 }
1038
1039 /// The parsed but unverified body part of a signed network document (working type)
1040 ///
1041 $/// Contains a $tname which has been parsed
1042 /// as part of a signed document,
1043 /// but the signatures aren't embodied here, and have not been verified.
1044 ///
1045 /// Not very useful to callers, who should use the `BodyUnverified` type instead,
1046 /// and its implementation of `NetdocParseable`.
1047 //
1048 // We implement NetdocParseable on FooUnverified using ItemStream::parse_signed.
1049 // ItemStream::parse_signed is a fairly normal but ad-hoc
1050 // implementation of NetdocParseable which uses as subroutines implementations
1051 // of NetdocParseable for the body and NetdocParseableSignatures for the signatures.
1052 //
1053 // We need a newtype because we don't want to implement `NetdocParseable`
1054 // for a type which is just the body. Such an impl would be usable by mistake,
1055 // via the top-level parse2 functions, and it would then simply discard the signatures
1056 // and return unverified data, bypassing our efforts to prevent such bugs.
1057 //
1058 // Ideally we would have a generic `UnverifiedParsedBody<B>` type or something
1059 // but then this macro, invoked in other crates, couldn't impl NetdocParseable for
1060 // UnverifiedParsedBody<TheirType>, due to trait coherence rules.
1061 //
1062 #[derive(derive_more::From)]
1063 pub struct $NETDOC_PARSEABLE_TTYPE<$tdefgens> {
1064 /// The unverified body
1065 unverified: $ttype,
1066 }
1067
1068 impl<$tgens> $P::NetdocParseable for $<$ttype Unverified> {
1069 fn doctype_for_error() -> &'static str {
1070 $NETDOC_PARSEABLE_TTYPE::doctype_for_error()
1071 }
1072
1073 fn is_intro_item_keyword(kw: $P::KeywordRef<'_>) -> bool {
1074 $NETDOC_PARSEABLE_TTYPE::is_intro_item_keyword(kw)
1075 }
1076
1077 fn is_structural_keyword(kw: $P::KeywordRef<'_>) -> Option<$P::IsStructural> {
1078 $NETDOC_PARSEABLE_TTYPE::is_structural_keyword(kw).or_else(
1079 || <$SIGS_TYPE as $P::NetdocParseableSignatures>
1080 ::is_item_keyword(kw).then_some($P::IsStructural)
1081 )
1082 }
1083
1084 fn from_items<'s>(
1085 input: &mut $P::ItemStream<'s>,
1086 outer_stop: $P::stop_at!(),
1087 ) -> $P::Result<$<$ttype Unverified>, $P::ErrorProblem> {
1088 $EMIT_DEBUG_PLACEHOLDER
1089 input.parse_signed(outer_stop)
1090 }
1091 }
1092
1093 impl<$tgens> $P::NetdocUnverified for $<$ttype Unverified> {
1094 type Body = $ttype;
1095 type Signatures = $SIGS_TYPE;
1096 fn inspect_unverified(&self) -> (&Self::Body, &$SIGS_DATA_TYPE) {
1097 (&self.body, &self.sigs)
1098 }
1099 fn unwrap_unverified(self) -> (Self::Body, $SIGS_DATA_TYPE) {
1100 (self.body, self.sigs)
1101 }
1102 fn from_parts(body: Self::Body, sigs: $SIGS_DATA_TYPE) -> Self {
1103 Self { body, sigs }
1104 }
1105 }
1106
1107 impl<$tgens> $P::HasUnverifiedParsedBody for $ttype {
1108 type UnverifiedParsedBody = $NETDOC_PARSEABLE_TTYPE;
1109 fn unverified_into_inner_unchecked(unverified: Self::UnverifiedParsedBody) -> Self {
1110 unverified.unverified
1111 }
1112 }
1113}
1114
1115//==================== ItemValueParseable user-facing derive macro ====================
1116//
1117// deftly template `ItemValueParseable`
1118//
1119// * entrypoint for deriving the `ItemValueParseable` and `SignatureItemParseable` traits
1120// * docs for the meta attributes we support during *item* parsing
1121// * implementation of those derives
1122
1123define_derive_deftly! {
1124 use NetdocDeriveAnyCommon;
1125 use NetdocParseAnyCommon;
1126 use NetdocItemDeriveCommon;
1127
1128 /// Derive `ItemValueParseable` (or `SignatureItemParseable`)
1129 ///
1130 // NB there is very similar wording in the ItemValueEncodable derive docs.
1131 // If editing any of this derive's documentation, considering editing that too.
1132 //
1133 /// Fields in the struct are parsed from the keyword line arguments,
1134 /// in the order they appear in the struct.
1135 ///
1136 /// ### Field type
1137 ///
1138 /// Each field should be:
1139 ///
1140 /// * `impl `[`ItemArgumentParseable`] (one argument),
1141 /// * `Option<impl ItemArgumentParseable>` (one optional argument),
1142 /// * `Vec<impl ItemArgumentParseable>` (zero or more arguments), or
1143 /// * `BTreeSet<impl ItemArgumentParseable + Ord>` (zero or more arguments).
1144 ///
1145 /// `ItemArgumentParseable` can be implemented via `impl FromStr`,
1146 /// by writing `impl NormalItemArgument`.
1147 ///
1148 /// For `Option` or `Vec`, we expect that *if* there are any further arguments,
1149 /// they are for this field.
1150 /// So absence of any optional argument means absence of following arguments,
1151 /// and no arguments can follow a `Vec`.
1152 ///
1153 /// Some Tor netdocs have optional arguments followed by other data,
1154 /// with unclear/ambiguous parsing rules.
1155 /// These cases typically require manual implementation of [`ItemValueParseable`].
1156 ///
1157 /// (Multiplicity is implemented via types in the [`multiplicity`] module,
1158 /// specifically [`ArgumentSetSelector`] and [`ArgumentSetMethods`].)
1159 ///
1160 /// ### Top-level attributes:
1161 ///
1162 /// * **`#[deftly(netdoc(no_extra_args))]**:
1163 ///
1164 /// Reject, rather than ignore, additional arguments found in the document
1165 /// which aren't described by the struct.
1166 ///
1167 /// * **`#[deftly(netdoc(signature(hash_accu = HASH_ACCU))]**:
1168 ///
1169 /// This item is a signature item.
1170 /// [`SignatureItemParseable`] will be implemented instead of [`ItemValueParseable`].
1171 ///
1172 /// **`HASH_ACCU`** is the type in which the hash(es) for this item will be accumulated,
1173 /// and must implement [`SignatureHashesAccumulator`].
1174 /// It is used as [`SignatureItemParseable::HashAccu`].
1175 ///
1176 /// * **`#[deftly(netdoc(debug))]`**:
1177 ///
1178 /// Currently implemented only as a placeholder
1179 ///
1180 /// The generated implementation may in future generate copious debug output
1181 /// to the program's stderr when it is run.
1182 /// Do not enable in production!
1183 ///
1184 $DOC_DEBUG_PLACEHOLDER
1185 ///
1186 /// ### Field-level attributes:
1187 ///
1188 /// * **`#[deftly(netdoc(rest))]**:
1189 ///
1190 /// The field is the whole rest of the line.
1191 /// Must come after any other normal argument fields.
1192 /// Only allowed once.
1193 ///
1194 /// The field type must implement `FromStr`.
1195 /// (I.e. `Vec` , `Option` etc., are not allowed, and `ItemArgumentParseable` is not used.)
1196 ///
1197 /// * **`#[deftly(netdoc(object))]**:
1198 ///
1199 /// The field is the Object.
1200 /// It must implement [`ItemObjectParseable`]
1201 /// (or be `Option<impl ItemObjectParseable>`).
1202 ///
1203 /// Only allowed once.
1204 /// If omittted, any object is rejected.
1205 ///
1206 /// * **`#[deftly(netdoc(object(label = "LABEL")))]**:
1207 ///
1208 /// Sets the expected label for an Object.
1209 /// If not supplied, uses [`ItemObjectParseable::check_label`].
1210 ///
1211 /// * **`#[deftly(netdoc(with = MODULE)]**:
1212 ///
1213 /// Instead of `ItemArgumentParseable`, the argument is parsed with `MODULE::from_args`,
1214 /// which must have the same signature as [`ItemArgumentParseable::from_args`].
1215 ///
1216 /// With `#[deftly(netdoc(rest))]`, the argument is parsed with `MODULE::from_args_rest`,
1217 /// must have the signature
1218 /// `fn from_args_rest(s: &str) -> Result<FIELD, _>`).
1219 /// and replaces `<FIELD as FromStr>::from_str`.
1220 ///
1221 /// With `#[deftly(netdoc(object))]`, uses `MODULE::try_from`
1222 /// which must have the signature `fn(Vec<u8>) -> Result<OBJECT, _>;
1223 /// like `TryFrom::<Vec<u8>>>::try_from`.
1224 /// LABEL must also be specified
1225 /// unless the object also implements `ItemObjectParseable`.
1226 /// Errors from parsing will all be collapsed into
1227 /// [`ErrorProblem::ObjectInvalidData`].
1228 ///
1229 /// * **`#[deftly(netdoc(skip))]**:
1230 ///
1231 /// Do not parse this field; fill it in with `Default::default()` instead.
1232 export ItemValueParseable for struct, meta_quoted rigorous, expect items, beta_deftly:
1233
1234 ${define TRAIT ${if T_IS_SIGNATURE { SignatureItemParseable } else { ItemValueParseable }}}
1235 ${define METHOD ${if T_IS_SIGNATURE { from_unparsed_and_body } else { from_unparsed }}}
1236
1237 // SignatureItemParseable::HashAccu
1238 ${define SIG_HASH_ACCU_TYPE ${tmeta(netdoc(signature(hash_accu))) as ty}}
1239
1240 impl<$tgens> $P::$TRAIT for $ttype {
1241 ${if T_IS_SIGNATURE {
1242 type HashAccu = $SIG_HASH_ACCU_TYPE;
1243 }}
1244
1245 fn $METHOD<'s>(
1246 mut input: $P::UnparsedItem<'s>,
1247 ${if T_IS_SIGNATURE {
1248 document_body: &SignatureHashInputs<'_>,
1249 hash_accu: &mut $SIG_HASH_ACCU_TYPE,
1250 }}
1251 ) -> $P::Result<Self, $P::EP>
1252 {
1253 #[allow(unused_imports)] // false positive when macro is used with prelude in scope
1254 use $P::*;
1255
1256 $DEFINE_DTRACE
1257
1258 dtrace!(
1259 "item start",
1260 input.keyword().as_str(),
1261 input.args_copy().into_remaining(),
1262 input.object().map(|o| o.label()),
1263 );
1264
1265 ${if T_IS_SIGNATURE {
1266 <$SIG_HASH_ACCU_TYPE as SignatureHashesAccumulator>::update_from_netdoc_body(
1267 hash_accu,
1268 document_body
1269 )?;
1270 }}
1271
1272 let object = input.object();
1273 #[allow(unused)]
1274 let mut args = input.args_mut();
1275 $(
1276 #[allow(non_snake_case)]
1277 let $fpatname = ${select1
1278 F_NORMAL { {
1279 let selector = MultiplicitySelector::<$ftype>::default();
1280 dtrace!(
1281 $"field $fname, normal",
1282 selector.argument_set_debug(),
1283 args.clone().into_remaining(),
1284 );
1285 ${if not(fmeta(netdoc(with))) {
1286 selector.${paste_spanned $fname check_argument_value_parseable}();
1287 }}
1288 selector.parse_with(
1289 &mut args,
1290 ${fmeta(netdoc(with))
1291 as path,
1292 default { ItemArgumentParseable }}::${paste_spanned $fname from_args},
1293 ).map_err(args.error_handler(stringify!($fname)))?
1294 } }
1295 F_OBJECT { {
1296 let selector = MultiplicitySelector::<$ftype>::default();
1297 dtrace!(
1298 $"field $fname, object",
1299 selector.object_set_debug(),
1300 object.as_ref().map(|o| (o.label(), o.decode_data().map(|d| d.len()))),
1301 );
1302 let object = object.map(|object| {
1303 let data = object.decode_data()?;
1304 ${if fmeta(netdoc(object(label))) {
1305 if object.label() != ${fmeta(netdoc(object(label))) as str} {
1306 return Err(EP::ObjectIncorrectLabel)
1307 }
1308 } else {
1309 selector.check_label(object.label())?;
1310 }}
1311 ${if fmeta(netdoc(with)) {
1312 ${fmeta(netdoc(with)) as path}::${paste_spanned $fname try_from}
1313 (data)
1314 .map_err(|_| EP::ObjectInvalidData)
1315 } else {
1316 selector.${paste_spanned $fname check_object_parseable}();
1317 ItemObjectParseable::from_bytes(&data)
1318 }}
1319 }).transpose()?;
1320 selector.resolve_option(object)?
1321 } }
1322 F_REST { {
1323 dtrace!($"field $fname, rest", args.clone().into_remaining());
1324 // consumes `args`, leading to compile error if the rest field
1325 // isn't last (or is combined with no_extra_args).
1326 let args_consume = args;
1327 ${if fmeta(netdoc(with)) {
1328 ${fmeta(netdoc(with)) as path}::${paste_spanned $fname from_args_rest}
1329 } else {
1330 <$ftype as FromStr>::from_str
1331 }}
1332 (args_consume.into_remaining())
1333 .map_err(|_| AE::Invalid)
1334 .map_err(args_consume.error_handler(stringify!($fname)))?
1335 } }
1336 F_SKIP { {
1337 <$ftype as Default>::default()
1338 } }
1339 };
1340 )
1341 ${if approx_equal({}, $( ${when F_OBJECT} $fname )) {
1342 if object.is_some() {
1343 return Err(EP::ObjectUnexpected);
1344 }
1345 }}
1346 ${if tmeta(netdoc(no_extra_args)) {
1347 args.reject_extra_args()?;
1348 }}
1349 dtrace!("item complete Ok");
1350 Ok($tname { $( $fname: $fpatname, ) })
1351 }
1352 }
1353}