macro_rules! derive_deftly_template_NetdocParseable {
({ $($driver:tt)* } [$($aoptions:tt)*] ($($future:tt)*) $($tpassthrough:tt)*) => { ... };
($($wrong:tt)*) => { ... };
}Expand description
Derive NetdocParseable for a document (or sub-document)
§Expected input structure
Should be applied named-field struct, where each field is an Item which may appear in the document, or a sub-document.
The first field will be the document’s intro Item. The expected Keyword for each Item will be kebab-case of the field name.
§Field type
Each field must be
implItemValueParseablefor an “exactly once” field,Vec<T: ItemValueParseable>for “zero or more”, orOption<T: ItemValueParseable>for “zero or one”.
We don’t directly support “at least once”: the parsed network document doesn’t imply the invariant that at least one such item was present.
(This is implemented via types in the multiplicity module,
specifically ItemSetSelector.)
§Signed documents
To handle signed documents define two structures:
Foo, containing only the content, not the signatures. DeriveNetdocParseableandNetdocSigned.FooSignatures, containing only the signatures. DeriveNetdocParseablewith#[deftly(netdoc(signatures))].
Don’t mix signature items with non-signature items in the same struct. (This wouldn’t compile, because the field type would implement the wrong trait.)
§Top-level attributes:
-
#[deftly(netdoc(doctype_for_error = "EXPRESSION"))]:Specifies the value to be returned from
NetdocParseable::doctype_for_error.Note, must be an expression, so for a literal, nested
""are needed.The default is the intro item keyword.
-
#[deftly(netdoc(signatures))]:This type is the signatures section of another document. Signature sections have no separate intro keyword: every field is structural and they are recognised in any order.
Fields must implement
SignatureItemParseable, rather thanItemValueParseable,This signatures sub-document will typically be included in a
FooSignedstruct derived withNetdocSigned, rather than included anywhere manually. -
#[deftly(netdoc(debug))]:The generated implementation will generate copious debug output to the program’s stderr when it is run. Do not enable in production!
§Field-level attributes:
-
#[deftly(netdoc(keyword = STR))]:Use
STRas the Keyword for this Item. -
#[deftly(netdoc(single_arg))]:The field type implements
ItemArgumentParseable, instead ofItemValueParseable, and is parsed as if(FIELD_TYPE,)had been written. -
#[deftly(netdoc(with = "MODULE"))]:Instead of
ItemValueParseable, the item is parsed withMODULE::from_unparsed, which must have the same signature asItemValueParseable::from_unparsed.(Not supported for sub-documents, signature items, or field collections.)
-
#[deftly(netdoc(default))]:This field is optional (“at most once”); if not present,
FIELD_TYPE::default()will be used.This is an alternative to declaring the field type as
OptionWithnetdoc(default), the field value doesn’t need unwrapping. WithOptionit is possible to see if the field was provided. -
#[deftly(netdoc(flatten))]:This field is a struct containing further individual normal fields. The Items for those individual fields can appear in this outer document in any order, interspersed with other normal fields.
The field type must implement
NetdocParseableFields. -
#[deftly(netdoc(subdoc))]:This field is a sub-document. The value type
Tmust implmentNetdocParseableinstead ofItemValueParseable.The field name is not used for parsging; the sub-document’s intro keyword is used instead.
Sub-documents are expected to appear after all normal items, in the order presented in the struct definition.
§Example
use derive_deftly::Deftly;
use tor_netdoc::derive_deftly_template_NetdocParseable;
use tor_netdoc::derive_deftly_template_NetdocSigned;
use tor_netdoc::derive_deftly_template_ItemValueParseable;
use tor_netdoc::parse2::{parse_netdoc, VerifyFailed};
use tor_netdoc::parse2::{SignatureItemParseable, SignatureHashInputs};
#[derive(Deftly, Debug, Clone)]
#[derive_deftly(NetdocParseable, NetdocSigned)]
pub struct NdThing {
pub thing_start: (),
pub value: (String,),
}
#[derive(Deftly, Debug, Clone)]
#[derive_deftly(NetdocParseable)]
#[deftly(netdoc(signatures))]
pub struct NdThingSignatures {
pub signature: FoolishSignature,
}
#[derive(Deftly, Debug, Clone)]
#[derive_deftly(ItemValueParseable)]
pub struct FoolishSignature {
pub doc_len: usize,
#[deftly(netdoc(sig_hash = "use_length_as_foolish_hash"))]
pub doc_len_actual_pretending_to_be_hash: usize,
}
fn use_length_as_foolish_hash(body: &SignatureHashInputs) -> usize {
body.body().body().len()
}
let doc_text =
r#"thing-start
value something
signature 28
"#;
impl NdThingSigned {
pub fn verify_foolish_timeless(self) -> Result<NdThing, VerifyFailed> {
let sig = &self.signatures.signature;
if sig.doc_len != sig.doc_len_actual_pretending_to_be_hash {
return Err(VerifyFailed::VerifyFailed);
}
Ok(self.body)
}
}
let doc: NdThingSigned = parse_netdoc(&doc_text, "<input>").unwrap();
let doc = doc.verify_foolish_timeless().unwrap();
assert_eq!(doc.value.0, "something");This is a derive_deftly template. Do not invoke it directly.
To use it, write: #[derive(Deftly)] #[derive_deftly(NetdocParseable)].