macro_rules! derive_deftly_template_NetdocParseable {
({ $($driver:tt)* } [$($aoptions:tt)*] ($($future:tt)*) $($tpassthrough:tt)*) => { ... };
($($wrong:tt)*) => { ... };
}
parse2
only.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
impl
ItemValueParseable
for 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. DeriveNetdocParseable
andNetdocSigned
.FooSignatures
, containing only the signatures. DeriveNetdocParseable
with#[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
FooSigned
struct 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
STR
as the Keyword for this Item. -
#[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
Option
Withnetdoc(default)
, the field value doesn’t need unwrapping. WithOption
it is possible to see if the field was provided. -
#[deftly(netdoc(subdoc))]
:This field is a sub-document. The value type
T
must implmentNetdocParseable
instead 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)]
.