derive_deftly_template_NetdocParseable

Macro derive_deftly_template_NetdocParseable 

Source
macro_rules! derive_deftly_template_NetdocParseable {
    ({ $($driver:tt)* } [$($aoptions:tt)*] ($($future:tt)*) $($tpassthrough:tt)*) => { ... };
    ($($wrong:tt)*) => { ... };
}
Available on crate feature 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”, or
  • Option<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. Derive NetdocParseable and NetdocSigned.
  • FooSignatures, containing only the signatures. Derive NetdocParseable 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 than ItemValueParseable,

    This signatures sub-document will typically be included in a FooSigned struct derived with NetdocSigned, 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 With netdoc(default), the field value doesn’t need unwrapping. With Option it is possible to see if the field was provided.

  • #[deftly(netdoc(subdoc))]:

    This field is a sub-document. The value type T must implment NetdocParseable instead of ItemValueParseable.

    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)].