//! Multiplicity of fields (Items and Arguments) //! //! This module supports type-based handling of multiplicity, //! of Items (within Documents) and Arguments (in Item keyword lines). //! //! It is **for use by macros**, rather than directly. //! //! See also `encode::multiplicity` which is the corresponding module for encoding. //! //! # Explanation //! //! We use autoref specialisation to allow macros to dispatch to //! trait impls for `Vec`, `Option` etc. //! as well as simply unadorned `T`. //! //! We implement traits on a helper type `struct `[`MultiplicitySelector`]. //! //! For Items we have `trait `[`ItemSetMethods`]. //! //! `ItemSetMethods` is implemented for `MultiplicitySelector` //! for each supported `Field`. //! So, for `MultiplicitySelector`, `MultiplicitySelector>`, and `MultiplicitySelector>`. //! *But*, for just `T`, the impl is on `&MultiplicitySelector`. //! //! When methods on `MultiplicitySelector` are called, the compiler finds //! the specific implementation for `MultiplicitySelector>` or `..Vec<_>`, //! or, failing that, derefs and finds the blanket impl on `&MultiplicitySelector`. //! //! For Arguments we have [`ArgumentSetMethods`], //! and for Objects, [`ObjectSetMethods`], //! which work similarly. //! //! (We need separate traits for each of the kinds of netdoc element, //! for good support of inference in the derive macro. //! Type inference is particularly difficult during parsing, since we need the type //! information to flow from the field type, which is the *destination* //! to which a value is going to be stored.) use super::*; use crate::types::RetainedOrderVec; #[cfg(doc)] use crate::encode; /// Helper type that allows us to select an impl of `ItemSetMethods` etc. /// /// **For use by macros**. /// /// See the [module-level docs](multiplicity). /// /// This is distinct from `encode::MultiplicitySelector`, /// principally because it has the opposite variance. #[derive(Educe)] #[educe(Clone, Copy, Default)] pub struct MultiplicitySelector(PhantomData Field>); /// Helper type that allows us to implement Debug /// /// Returned by [`ItemSetMethods::item_set_debug`] etc., /// using information from [`ItemSetMethods::debug_core`] etc. #[derive(derive_more::Debug)] #[debug("{}={}", self.0, self.1)] #[allow(dead_code)] // yes, they are only read by the Debug impl - that's what they're for! struct DebugHelper( /// scope, eg `items` &'static str, /// multiplicity type pattern, eg `Vec<_>` or `1` &'static str, ); /// Methods for handling some multiplicity of Items, during parsing /// /// **For use by macros**. /// /// During parsing, we accumulate into a value of type `Option`. /// The semantics of this are item-set-implementation-dependent; /// using a type which is generic over the field type in a simple way /// allows the partially-parsed accumulation state for a whole netdoc to have a concrete type. /// /// See the [module-level docs](multiplicity), and /// [Field type in `NetdocParseable`](derive_deftly_template_NetdocParseable#field-type). /// /// # Example /// /// The code in the (derive) macro output is roughly like this: /// /// ``` /// use tor_netdoc::parse2::multiplicity::{MultiplicitySelector, ItemSetMethods as _}; /// /// let selector = MultiplicitySelector::>::default(); /// let mut accum = None; /// selector.accumulate(&mut accum, 12).unwrap(); /// let out = selector.finish(accum, "item-set").unwrap(); /// /// assert_eq!(out, [12]); /// ``` // // When implementing this, update the documentation in the `NetdocParseable` derive. pub trait ItemSetMethods: Copy + Sized { /// The value for each Item. /// /// Should match the corresponding /// [`encode::MultiplicityMethods::Each`]. /// (See docs there for rationale.) type Each: Sized; /// The output type: the type of the field in the netdoc struct. type Field: Sized; /// Can we accumulate another item ? /// /// Can be used to help predict whether `accumulate` will throw. fn can_accumulate(self, acc: &Option) -> Result<(), EP>; /// Accumulate one value into the accumulator. fn accumulate(self, acc: &mut Option, one: Self::Each) -> Result<(), EP>; /// Multiplicity representation for `#[deftly(netdoc(debug))]` output, core /// /// Should generally be in a form like `Vec<_>`. /// /// See also [`ItemSetMethods::item_set_debug`], which is what the derives call. // // This can't be a `Debug` supertrait, because that won't work // with the `&'_ MultiplicitySelector` impl. fn debug_core(self) -> &'static str; /// Multiplicity representation for `#[deftly(netdoc(debug))]` output /// /// This adds a bit framing and type-fu that allows the derive macro's /// call to be as simple as possible. /// /// See also [`ItemSetMethods::debug_core`], which is what each multiplicity implements. // // dtrace!, which we use for debugging in the parser macros, doesn't print variable names, // thinking things are probably obvious enough. But for the elector here we want to // include `items=`. fn item_set_debug(self) -> impl Debug { DebugHelper("items", self.debug_core()) } /// Resolve the accumulator into the output. fn finish( self, acc: Option, item_keyword: &'static str, ) -> Result; /// If the contained type is a sub-document, call its `is_intro_item_keyword`. fn is_intro_item_keyword(self, kw: KeywordRef<'_>) -> bool where Self::Each: NetdocParseable, { Self::Each::is_intro_item_keyword(kw) } /// If the contained type is a sub-document, call its `is_structural_keyword`. fn is_structural_keyword(self, kw: KeywordRef<'_>) -> Option where Self::Each: NetdocParseable, { Self::Each::is_structural_keyword(kw) } /// `finish` for if the contained type is a wsub-document /// /// Obtain the sub-document's intro keyword from its `doctype_for_error`. fn finish_subdoc(self, acc: Option) -> Result where Self::Each: NetdocParseable, { self.finish(acc, Self::Each::doctype_for_error()) } /// Check that the element type is an Item /// /// For providing better error messages when struct fields don't implement the right trait. /// See `derive.rs`, and search for this method name. fn check_item_value_parseable(self) where Self::Each: ItemValueParseable, { } /// Check that the element type is a Signature fn check_signature_item_parseable(self, _: &mut H) where Self::Each: SignatureItemParseable, H: AsMut<::HashAccu>, { } /// Check that the element type is a sub-document fn check_subdoc_parseable(self) where Self::Each: NetdocParseable, { } /// Check that the element type is an argument fn check_item_argument_parseable(self) where Self::Each: ItemArgumentParseable, { } } impl ItemSetMethods for MultiplicitySelector> { type Each = T; type Field = Vec; // We always have None, or Some(nonempty) fn can_accumulate(self, _acc: &Option>) -> Result<(), EP> { Ok(()) } fn accumulate(self, acc: &mut Option>, item: T) -> Result<(), EP> { acc.get_or_insert_default().push(item); Ok(()) } fn finish(self, acc: Option>, _keyword: &'static str) -> Result, EP> { Ok(acc.unwrap_or_default()) } fn debug_core(self) -> &'static str { "Vec<_>" } } impl ItemSetMethods for MultiplicitySelector> { type Each = T; type Field = RetainedOrderVec; // We always have None, or Some(nonempty) fn can_accumulate(self, _acc: &Option>) -> Result<(), EP> { Ok(()) } fn accumulate(self, acc: &mut Option>, item: T) -> Result<(), EP> { acc.get_or_insert_default().0.push(item); Ok(()) } fn finish( self, acc: Option>, _keyword: &'static str, ) -> Result, EP> { Ok(acc.unwrap_or_default()) } fn debug_core(self) -> &'static str { "RetainedOrderVec<_>" } } impl ItemSetMethods for MultiplicitySelector> { type Each = T; type Field = BTreeSet; // We always have None, or Some(nonempty) fn can_accumulate(self, _acc: &Option>) -> Result<(), EP> { Ok(()) } fn accumulate(self, acc: &mut Option>, item: T) -> Result<(), EP> { if !acc.get_or_insert_default().insert(item) { return Err(EP::ItemRepeated); } Ok(()) } fn finish(self, acc: Option>, _keyword: &'static str) -> Result, EP> { Ok(acc.unwrap_or_default()) } fn debug_core(self) -> &'static str { "BTreeSet<_>" } } impl ItemSetMethods for MultiplicitySelector> { type Each = T; type Field = Option; // We always have None, or Some(Some(_)) fn can_accumulate(self, acc: &Option>) -> Result<(), EP> { if acc.is_some() { Err(EP::ItemRepeated)?; } Ok(()) } // We always have None, or Some(Some(_)) fn accumulate(self, acc: &mut Option>, item: T) -> Result<(), EP> { self.can_accumulate(acc)?; *acc = Some(Some(item)); Ok(()) } fn finish(self, acc: Option>, _keyword: &'static str) -> Result, EP> { Ok(acc.flatten()) } fn debug_core(self) -> &'static str { "Option<_>" } } impl ItemSetMethods for &'_ MultiplicitySelector { type Each = T; type Field = T; fn can_accumulate(self, acc: &Option) -> Result<(), EP> { if acc.is_some() { Err(EP::ItemRepeated)?; } Ok(()) } fn accumulate(self, acc: &mut Option, item: T) -> Result<(), EP> { self.can_accumulate(acc)?; *acc = Some(item); Ok(()) } fn finish(self, acc: Option, keyword: &'static str) -> Result { acc.ok_or(EP::MissingItem { keyword }) } fn debug_core(self) -> &'static str { // This appears in #[deftly(netdoc(debug))] output for singleton fields. // We probably don't want the macros' users to have to think about our // autoref-specialisation. So we don't write anything about `&` here. "1" } } /// Method for handling some multiplicity of Arguments /// /// **For use by macros**. /// /// See the [module-level docs](multiplicity), and /// [Field type in `ItemValueParseable`](derive_deftly_template_ItemValueParseable#field-type). /// /// # Example /// /// The code in the (derive) macro output is roughly like this: /// /// ``` /// use tor_netdoc::parse2::multiplicity::{MultiplicitySelector, ArgumentSetMethods as _}; /// use tor_netdoc::parse2::{ItemArgumentParseable, ItemStream, ParseInput}; /// let doc = "intro-item 12 66\n"; /// let input = ParseInput::new(doc, ""); /// let mut items = ItemStream::new(&input).unwrap(); /// let mut item = items.next().unwrap().unwrap(); /// /// let args = MultiplicitySelector::>::default() /// .parse_with(item.args_mut(), ItemArgumentParseable::from_args) /// .unwrap(); /// assert_eq!(args, [12, 66]); /// ``` // // When implementing this, update the documentation in the `ItemValueParseable` derive. pub trait ArgumentSetMethods: Copy + Sized { /// The value for each Item. /// /// Should match the corresponding /// [`encode::MultiplicityMethods::Each`]. /// (See docs there for rationale.) type Each: Sized; /// The output type: the type of the field in the Item struct. /// /// This is *not* the type of an individual netdoc argument; /// that is not explicitly represented in the trait. type Field: Sized; /// Parse zero or more argument(s) into `Self::Field`. fn parse_with

(self, args: &mut ArgumentStream<'_>, parser: P) -> Result where P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result; /// Multiplicity representation for `#[deftly(netdoc(debug))]` output, core /// /// Should generally be in a form like `Vec<_>`. /// /// See [`ItemSetMethods::debug_core`] and [`ArgumentSetMethods::argument_set_debug`]. fn debug_core(self) -> &'static str; /// Multiplicity representation for `#[deftly(netdoc(debug))]` output /// /// See [`ItemSetMethods::item_set_debug`] and [`ArgumentSetMethods::debug_core`]. fn argument_set_debug(self) -> impl Debug { DebugHelper("args", self.debug_core()) } /// Check that the element type is an Argument /// /// For providing better error messages when struct fields don't implement the right trait. /// See `derive.rs`, and search for this method name. fn check_argument_value_parseable(self) where Self::Each: ItemArgumentParseable, { } } impl ArgumentSetMethods for MultiplicitySelector> { type Each = T; type Field = Vec; fn parse_with

(self, args: &mut ArgumentStream<'_>, parser: P) -> Result where P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result, { let mut acc = vec![]; while args.something_to_yield() { acc.push(parser(args)?); } Ok(acc) } fn debug_core(self) -> &'static str { "Vec<_>" } } impl ArgumentSetMethods for MultiplicitySelector> { type Each = T; type Field = BTreeSet; fn parse_with

(self, args: &mut ArgumentStream<'_>, parser: P) -> Result where P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result, { let mut acc = BTreeSet::new(); while args.something_to_yield() { if !acc.insert(parser(args)?) { return Err(AE::Invalid); } } Ok(acc) } fn debug_core(self) -> &'static str { "BTreeSet<_>" } } impl ArgumentSetMethods for MultiplicitySelector> { type Each = T; type Field = Option; fn parse_with

(self, args: &mut ArgumentStream<'_>, parser: P) -> Result where P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result, { if !args.something_to_yield() { return Ok(None); } Ok(Some(parser(args)?)) } fn debug_core(self) -> &'static str { "Option<_>" } } impl ArgumentSetMethods for &MultiplicitySelector { type Each = T; type Field = T; fn parse_with

(self, args: &mut ArgumentStream<'_>, parser: P) -> Result where P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result, { parser(args) } fn debug_core(self) -> &'static str { "1" } } /// Method for handling some multiplicity of Objects /// /// **For use by macros**. /// /// See the [module-level docs](multiplicity), and /// [Field type in `ItemValueParseable`](derive_deftly_template_ItemValueParseable#field-type). /// /// # Example /// /// The code in the (derive) macro output is roughly like this: /// /// ``` /// use tor_netdoc::parse2::multiplicity::{MultiplicitySelector, ObjectSetMethods as _}; /// use tor_netdoc::parse2::{ItemStream, ParseInput}; /// let doc = "intro-item\n-----BEGIN OBJECT-----\naGVsbG8=\n-----END OBJECT-----\n"; /// let input = ParseInput::new(doc, ""); /// let mut items = ItemStream::new(&input).unwrap(); /// let mut item = items.next().unwrap().unwrap(); /// /// let selector = MultiplicitySelector::>::default(); /// let obj = item.object().map(|obj| { /// let data = obj.decode_data().unwrap(); /// String::from_utf8(data) /// }).transpose().unwrap(); /// let obj = selector.resolve_option(obj).unwrap(); /// assert_eq!(obj, Some("hello".to_owned())); /// ``` pub trait ObjectSetMethods: Copy + Sized { /// The value for each Item. /// /// Should match the corresponding /// [`encode::OptionalityMethods::Each`]. /// (See [`encode::MultiplicityMethods::Each`] for rationale.) type Each: Sized; /// The output type: the type of the field in the Item struct. type Field: Sized; /// Parse zero or more argument(s) into `Self::Field`. fn resolve_option(self, found: Option) -> Result; /// Multiplicity representation for `#[deftly(netdoc(debug))]` output, core /// /// Should generally be in a form like `Option<_>`. /// /// See [`ItemSetMethods::debug_core`] and [`ObjectSetMethods::object_set_debug`]. fn debug_core(self) -> &'static str; /// Multiplicity representation for `#[deftly(netdoc(debug))]` output /// /// See [`ItemSetMethods::item_set_debug`] and [`ObjectSetMethods::debug_core`]. fn object_set_debug(self) -> impl Debug { DebugHelper("object", self.debug_core()) } /// If the contained type is `ItemObjectParseable`, call its `check_label` fn check_label(self, label: &str) -> Result<(), EP> where Self::Each: ItemObjectParseable, { Self::Each::check_label(label) } /// Check that the contained type can be parsed as an object fn check_object_parseable(self) where Self::Each: ItemObjectParseable, { } } impl ObjectSetMethods for MultiplicitySelector> { type Field = Option; type Each = T; fn resolve_option(self, found: Option) -> Result { Ok(found) } fn debug_core(self) -> &'static str { "Option<_>" } } impl ObjectSetMethods for &MultiplicitySelector { type Field = T; type Each = T; fn resolve_option(self, found: Option) -> Result { found.ok_or(EP::MissingObject) } fn debug_core(self) -> &'static str { "1" } }