//! Multiplicity for encoding netdoc elements, via ad-hoc deref specialisation. //! //! 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 `parse2::multiplicity` which is the corresponding module for parsing. //! //! # Explanation //! //! We use autoref specialisation to allow macros to dispatch to //! trait impls for `Vec`, `Option` etc. as well as simply unadorned `T`. //! //! 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 Objects, where only `T` and `Option` are allowed, //! we use `OptionalityMethods`. //! //! We implement traits on helper types `struct `[`MultiplicitySelector`], //! [`DeterminedMultiplicitySelector`] and [`SingletonMultiplicitySelector`]. //! //! The three selector types allow us to force the compiler to nail down the multiplicity, //! during type inference, before considering whether the "each" type implements the //! required trait. //! //! This is done by calling the `.selector()` method: //! deref specialisation and inherent method vs trait method priority selects //! the appropriate `.selector()` method, giving *another* selector, //! so that the compiler only considers other selector's `MultiplicityMethods`, //! when `.check_...` methods are used. //! Otherwise, when a field has type (say) `Vec`, //! a call to `.check_item_value_encodable` could be resolved by autoref //! so the compiler reports that **`Vec<..>`** doesn't implement the needed trait. //! We prevent this by having //! [`MultiplicitySelector::>::default().selector()`](MultiplicitySelector::>::selector) //! be an inherent method returning [`DeterminedMultiplicitySelector`]. //! //! `SingletonMultiplicitySelector` is used explicitly in the derive when we //! know that we want to encode exactly one element: //! for example, a document's intro item cannot be repeated or omitted. use super::*; use crate::types::RetainedOrderVec; #[cfg(doc)] use crate::parse2; /// Helper type that allows us to select an impl of `MultiplicityMethods` /// /// **For use by macros**. /// /// This is distinct from `parse2::MultiplicitySelector`, /// principally because it has the opposite variance. #[derive(Educe)] #[educe(Clone, Copy, Default)] pub struct MultiplicitySelector(PhantomData); /// Helper type implementing `MultiplicityMethods`, after the multiplicity is determined /// /// **For use by macros**. #[derive(Educe)] #[educe(Clone, Copy, Default)] pub struct DeterminedMultiplicitySelector(PhantomData); /// Helper type implementing `MultiplicityMethods`, when a field is statically a singleton /// /// **For use by macros**. #[derive(Educe)] #[educe(Clone, Copy, Default)] pub struct SingletonMultiplicitySelector(PhantomData); /// Methods for handling some multiplicity of netdoc elements, during encoding /// /// **For use by macros**. /// /// Each multiplicity impl allows us to iterate over the element(s). /// /// Methods are also provided for typechecking, which are used by the derive macro to /// produce reasonable error messages when a trait impl is missing. // // When adding features here, for example by implementing this trait, // update the documentation in the `NetdocEncodable` and `ItemValueEncodable` derives. pub trait MultiplicityMethods<'f>: Copy + Sized { /// The value for each thing. /// /// Should match the corresponding /// [`parse2::multiplicity::ItemSetMethods::Each`], /// [`parse2::multiplicity::ArgumentSetMethods::Each`], /// for consistency, and for the benefit of `with =` attributes referring to type names. // // For example, if these Each types don't match, then if you want to say // `with = ns_type( Each, SomethingSpecial, ... )` // so that the plain consensus just uses the normal parsing, it doesn't // work, because `Each` has to match both `parse2::multiplicity::ItemSetSelector::Each` // and `encode::MultiplicityMethods::Each`, or the derived parsing code gets type errors. // // Having them different is anomalous, anyway. type Each: Sized + 'f; /// The input type: the type of the field in the netdoc or item struct. type Field: Sized; /// Return the appropriate implementor of `MultiplicityMethods` fn selector(self) -> Self { self } /// Yield the items, in a stable order fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator + 'f; /// Cause a compiler error if the element is not `NetdocEncodable` fn check_netdoc_encodable(self) where Self::Each: NetdocEncodable, { } /// Cause a compiler error if the element is not `ItemValueEncodable` fn check_item_value_encodable(self) where Self::Each: ItemValueEncodable, { } /// Cause a compiler error if the element is not `ItemArgument` fn check_item_argument_encodable(self) where Self::Each: ItemArgument, { } /// Cause a compiler error if the element is not `ItemObjectEncodable` fn check_item_object_encodable(self) where Self::Each: ItemObjectEncodable, { } } impl MultiplicitySelector> { /// Return the appropriate implementor of `MultiplicityMethods` /// /// This is an inherent method so that it doesn't need the `EncodeOrd` bounds: /// that way if `EncodeOrd` is not implemented, we get a message about that, /// rather than a complaint that `ItemValueEncodable` isn't impl for `Vec`. pub fn selector(self) -> DeterminedMultiplicitySelector> { DeterminedMultiplicitySelector::default() } } impl<'f, T: EncodeOrd + 'f> MultiplicityMethods<'f> for DeterminedMultiplicitySelector> { type Each = T; type Field = Vec; fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator { let mut v = f.iter().collect_vec(); v.sort_by(|a, b| a.encode_cmp(*b)); v.into_iter() } } impl<'f, T: 'f> MultiplicityMethods<'f> for MultiplicitySelector> { type Each = T; type Field = RetainedOrderVec; fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator { f.0.iter() } } impl<'f, T: 'f> MultiplicityMethods<'f> for MultiplicitySelector> { type Each = T; type Field = BTreeSet; fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator { f.iter() } } impl<'f, T: 'f> MultiplicityMethods<'f> for MultiplicitySelector> { type Each = T; type Field = Option; fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator + 'f { f.iter() } } impl<'f, T: 'f> MultiplicityMethods<'f> for &'_ MultiplicitySelector { type Each = T; type Field = T; fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator + 'f { iter::once(f) } } impl<'f, T: 'f> MultiplicityMethods<'f> for SingletonMultiplicitySelector { type Each = T; type Field = T; fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator + 'f { iter::once(f) } } impl SingletonMultiplicitySelector { /// Test whether the value is `Default` pub fn is_default(self, item: &T) -> bool where T: Default + Eq, { item == &Default::default() } } /// Methods for handling optionality of a netdoc Object, during encoding /// // This could be used for things other than Object, if there were any thing // that supported Option but not Vec. // /// **For use by macros**. /// /// Each impl allows us to visit an optional element. pub trait OptionalityMethods: Copy + Sized { /// The possibly-present element. /// /// Should match the corresponding /// [`parse2::multiplicity::ObjectSetMethods::Each`]. /// (See [`MultiplicityMethods::Each`] for rationale.) type Each: Sized + 'static; /// The input type: the type of the field in the item struct. type Field: Sized; /// Yield the element, if there is one fn as_option<'f>(self, f: &'f Self::Field) -> Option<&'f Self::Each>; } impl OptionalityMethods for MultiplicitySelector> { type Each = T; type Field = Option; fn as_option<'f>(self, f: &'f Self::Field) -> Option<&'f Self::Each> { f.as_ref() } } impl OptionalityMethods for &'_ MultiplicitySelector { type Each = T; type Field = T; fn as_option<'f>(self, f: &'f Self::Field) -> Option<&'f Self::Each> { Some(f) } }