//! Core model for netdoc parsing use super::*; /// A document or section that can be parsed /// /// Normally [derived](derive_deftly_template_NetdocParseable). pub trait NetdocParseable: Sized { /// Document type for errors, normally its intro keyword fn doctype_for_error() -> &'static str; /// Is `Keyword` an intro Item Keyword for this kind of document? /// /// This is used with 1-keyword lookahead, to allow us to push or pop /// the parsing state into or out of a sub-document. /// /// For signatures sections, this should report *every* recognised keyword. fn is_intro_item_keyword(kw: KeywordRef<'_>) -> bool; /// Parse the document from a stream of Items /// /// Should stop before reading any keyword matching `stop_at`. /// (Except, right at the start.) /// /// Should also stop before reading a 2nd intro keyword, /// so that successive calls to this function can parse /// successive sub-documents of this kind. /// /// Otherwise, should continue until EOF. /// /// Must check whether the first item is this document's `is_intro_item_keyword`, /// and error if not. fn from_items(input: &mut ItemStream<'_>, stop_at: stop_at!()) -> Result; } /// A network document with (unverified) signatures /// /// Typically implemented automatically, for `FooSigned` structs, as defined by /// [`#[derive_deftly(NetdocSigned)]`](derive_deftly_template_NetdocSigned). pub trait NetdocSigned { /// The body, ie not including the signatures type Body: Sized; /// The signatures (the whole signature section) type Signatures: Sized; /// Inspect the document (and its signatures) /// /// # Security hazard /// /// The signature has not been verified, so the returned data must not be trusted. fn inspect_unverified(&self) -> (&Self::Body, &Self::Signatures); /// Obtain the actual document (and signatures), without verifying /// /// # Security hazard /// /// The signature has not been verified, so the returned data must not be trusted. fn unwrap_unverified(self) -> (Self::Body, Self::Signatures); /// Construct a new `NetdocSigned` from a body and signatures /// /// (Called by code generated by `#[derive_deftly(NetdocSigned)]`.) fn from_parts(body: Self::Body, signatures: Self::Signatures) -> Self; } /// An item (value) that can appear in a netdoc /// /// This is the type `T` of a field `item: T` in a netdoc type. /// /// An implementation is provided for tuples of `ItemArgumentParseable`, /// which parses each argument in turn, /// ignores additional arguments, /// and rejects any Object. /// /// Typically derived with /// [`#[derive_deftly(ItemValueParseable)]`](derive_deftly_template_ItemValueParseable). /// /// Signature items are special, and implement [`SignatureItemParseable`] instead. pub trait ItemValueParseable: Sized { /// Parse the item's value fn from_unparsed(item: UnparsedItem<'_>) -> Result; } /// An (individual) argument that can appear in a netdoc /// /// An implementations is provided for **`T: FromStr`**, /// which expects a single argument and passes it to `FromStr`. /// /// For netdoc arguments whose specified syntax spans multiple space-separated words, /// use a manual implementation or a wrapper type. pub trait ItemArgumentParseable: Sized { /// Parse the argument fn from_args<'s>( args: &mut ArgumentStream<'s>, field: &'static str, ) -> Result; } /// A possibly-optional Object value that can appear in netdoc /// /// Implemented for `Option`, so that `field: Option` /// allows parsing an optional object. pub trait ItemObjectParseable: Sized { /// Check that the Label is right fn check_label(label: &str) -> Result<(), ErrorProblem>; /// Convert the bytes of the Object (which was present) into the actual value /// /// `input` has been base64-decoded. fn from_bytes(input: &[u8]) -> Result; /// Convert the bytes of the Object, if any, into the actual value /// /// If there was an Object, `input` has been base64-decoded. /// If there was no Object, `input` is `None`. /// /// The provided implementation considers a missing object to be an error. fn from_bytes_option(input: Option<&[u8]>) -> Result { Self::from_bytes(input.ok_or(EP::MissingObject)?) } } //---------- provided blanket impls ---------- impl ItemObjectParseable for Option { fn check_label(label: &str) -> Result<(), EP> { T::check_label(label) } fn from_bytes(input: &[u8]) -> Result { Ok(Some(T::from_bytes(input)?)) } fn from_bytes_option(input: Option<&[u8]>) -> Result { let Some(input) = input else { return Ok(None) }; Self::from_bytes(input) } } impl ItemArgumentParseable for T { fn from_args<'s>(args: &mut ArgumentStream<'s>, field: &'static str) -> Result { let v = args .next() .ok_or(EP::MissingArgument { field })? .parse() .map_err(|_e| EP::InvalidArgument { field })?; Ok(v) } } /// implement [`ItemValueParseable`] for a particular tuple size macro_rules! item_value_parseable_for_tuple { { $($i:literal)* } => { paste! { impl< $( []: ItemArgumentParseable, )* > ItemValueParseable for ( $( [], )* ) { fn from_unparsed( #[allow(unused_mut)] mut item: UnparsedItem<'_>, ) -> Result { let r = ( $( <[] as ItemArgumentParseable>::from_args( item.args_mut(), stringify!($i), )?, )* ); if item.object().is_some() { return Err(EP::ObjectUnexpected) } Ok(r) } } } } } item_value_parseable_for_tuple! {} item_value_parseable_for_tuple! { 0 } item_value_parseable_for_tuple! { 0 1 } item_value_parseable_for_tuple! { 0 1 2 } item_value_parseable_for_tuple! { 0 1 2 3 } item_value_parseable_for_tuple! { 0 1 2 3 4 } item_value_parseable_for_tuple! { 0 1 2 3 4 5 } item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 } item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 } item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 8 } item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 8 9 }