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