Skip to main content

tor_netdoc/encode/
multiplicity.rs

1//! Multiplicity for encoding netdoc elements, via ad-hoc deref specialisation.
2//!
3//! This module supports type-based handling of multiplicity,
4//! of Items (within Documents) and Arguments (in Item keyword lines).
5//!
6//! It is **for use by macros**, rather than directly.
7//!
8//! See also `parse2::multiplicity` which is the corresponding module for parsing.
9//!
10//! # Explanation
11//!
12//! We use autoref specialisation to allow macros to dispatch to
13//! trait impls for `Vec<T>`, `Option<T>` etc. as well as simply unadorned `T`.
14//!
15//! When methods on `MultiplicitySelector` are called, the compiler finds
16//! the specific implementation for `MultiplicitySelector<Option<_>>` or `..Vec<_>`,
17//! or, failing that, derefs and finds the blanket impl on `&MultiplicitySelector<T>`.
18//!
19//! For Objects, where only `T` and `Option<T>` are allowed,
20//! we use `OptionalityMethods`.
21//!
22//! We implement traits on helper types `struct `[`MultiplicitySelector<Field>`],
23//! [`DeterminedMultiplicitySelector`] and [`SingletonMultiplicitySelector`].
24//!
25//! The three selector types allow us to force the compiler to nail down the multiplicity,
26//! during type inference, before considering whether the "each" type implements the
27//! required trait.
28//!
29//! This is done by calling the `.selector()` method:
30//! deref specialisation and inherent method vs trait method priority selects
31//! the appropriate `.selector()` method, giving *another* selector,
32//! so that the compiler only considers other selector's `MultiplicityMethods`,
33//! when `.check_...` methods are used.
34//! Otherwise, when a field has type (say) `Vec<NotItemValueParseable>`,
35//! a call to `.check_item_value_encodable` could be resolved by autoref
36//! so the compiler reports that **`Vec<..>`** doesn't implement the needed trait.
37//! We prevent this by having
38//! [`MultiplicitySelector::<Vec<_>>::default().selector()`](MultiplicitySelector::<Vec<T>>::selector)
39//! be an inherent method returning [`DeterminedMultiplicitySelector`].
40//!
41//! `SingletonMultiplicitySelector` is used explicitly in the derive when we
42//! know that we want to encode exactly one element:
43//! for example, a document's intro item cannot be repeated or omitted.
44
45use super::*;
46use crate::types::RetainedOrderVec;
47
48#[cfg(doc)]
49use crate::parse2;
50
51/// Helper type that allows us to select an impl of `MultiplicityMethods`
52///
53/// **For use by macros**.
54///
55/// This is distinct from `parse2::MultiplicitySelector`,
56/// principally because it has the opposite variance.
57#[derive(Educe)]
58#[educe(Clone, Copy, Default)]
59pub struct MultiplicitySelector<Field>(PhantomData<fn(Field)>);
60
61/// Helper type implementing `MultiplicityMethods`, after the multiplicity is determined
62///
63/// **For use by macros**.
64#[derive(Educe)]
65#[educe(Clone, Copy, Default)]
66pub struct DeterminedMultiplicitySelector<Field>(PhantomData<fn(Field)>);
67
68/// Helper type implementing `MultiplicityMethods`, when a field is statically a singleton
69///
70/// **For use by macros**.
71#[derive(Educe)]
72#[educe(Clone, Copy, Default)]
73pub struct SingletonMultiplicitySelector<Field>(PhantomData<fn(Field)>);
74
75/// Methods for handling some multiplicity of netdoc elements, during encoding
76///
77/// **For use by macros**.
78///
79/// Each multiplicity impl allows us to iterate over the element(s).
80///
81/// Methods are also provided for typechecking, which are used by the derive macro to
82/// produce reasonable error messages when a trait impl is missing.
83//
84// When adding features here, for example by implementing this trait,
85// update the documentation in the `NetdocEncodable` and `ItemValueEncodable` derives.
86pub trait MultiplicityMethods<'f>: Copy + Sized {
87    /// The value for each thing.
88    ///
89    /// Should match the corresponding
90    /// [`parse2::multiplicity::ItemSetMethods::Each`],
91    /// [`parse2::multiplicity::ArgumentSetMethods::Each`],
92    /// for consistency, and for the benefit of `with =` attributes referring to type names.
93    //
94    // For example, if these Each types don't match, then if you want to say
95    //  `with = ns_type( Each, SomethingSpecial, ... )`
96    // so that the plain consensus just uses the normal parsing, it doesn't
97    // work, because `Each` has to match both `parse2::multiplicity::ItemSetSelector::Each`
98    // and `encode::MultiplicityMethods::Each`, or the derived parsing code gets type errors.
99    //
100    // Having them different is anomalous, anyway.
101    type Each: Sized + 'f;
102
103    /// The input type: the type of the field in the netdoc or item struct.
104    type Field: Sized;
105
106    /// Return the appropriate implementor of `MultiplicityMethods`
107    fn selector(self) -> Self {
108        self
109    }
110
111    /// Yield the items, in a stable order
112    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> + 'f;
113
114    /// Cause a compiler error if the element is not `NetdocEncodable`
115    fn check_netdoc_encodable(self)
116    where
117        Self::Each: NetdocEncodable,
118    {
119    }
120    /// Cause a compiler error if the element is not `ItemValueEncodable`
121    fn check_item_value_encodable(self)
122    where
123        Self::Each: ItemValueEncodable,
124    {
125    }
126    /// Cause a compiler error if the element is not `ItemArgument`
127    fn check_item_argument_encodable(self)
128    where
129        Self::Each: ItemArgument,
130    {
131    }
132    /// Cause a compiler error if the element is not `ItemObjectEncodable`
133    fn check_item_object_encodable(self)
134    where
135        Self::Each: ItemObjectEncodable,
136    {
137    }
138}
139
140impl<T> MultiplicitySelector<Vec<T>> {
141    /// Return the appropriate implementor of `MultiplicityMethods`
142    ///
143    /// This is an inherent method so that it doesn't need the `EncodeOrd` bounds:
144    /// that way if `EncodeOrd` is not implemented, we get a message about that,
145    /// rather than a complaint that `ItemValueEncodable` isn't impl for `Vec<T>`.
146    pub fn selector(self) -> DeterminedMultiplicitySelector<Vec<T>> {
147        DeterminedMultiplicitySelector::default()
148    }
149}
150impl<'f, T: EncodeOrd + 'f> MultiplicityMethods<'f> for DeterminedMultiplicitySelector<Vec<T>> {
151    type Each = T;
152    type Field = Vec<T>;
153    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
154        let mut v = f.iter().collect_vec();
155        v.sort_by(|a, b| a.encode_cmp(*b));
156        v.into_iter()
157    }
158}
159impl<'f, T: 'f> MultiplicityMethods<'f> for MultiplicitySelector<RetainedOrderVec<T>> {
160    type Each = T;
161    type Field = RetainedOrderVec<T>;
162    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
163        f.0.iter()
164    }
165}
166impl<'f, T: 'f> MultiplicityMethods<'f> for MultiplicitySelector<BTreeSet<T>> {
167    type Each = T;
168    type Field = BTreeSet<T>;
169    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
170        f.iter()
171    }
172}
173impl<'f, T: 'f> MultiplicityMethods<'f> for MultiplicitySelector<Option<T>> {
174    type Each = T;
175    type Field = Option<T>;
176    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> + 'f {
177        f.iter()
178    }
179}
180impl<'f, T: 'f> MultiplicityMethods<'f> for &'_ MultiplicitySelector<T> {
181    type Each = T;
182    type Field = T;
183    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> + 'f {
184        iter::once(f)
185    }
186}
187impl<'f, T: 'f> MultiplicityMethods<'f> for SingletonMultiplicitySelector<T> {
188    type Each = T;
189    type Field = T;
190    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> + 'f {
191        iter::once(f)
192    }
193}
194impl<T> SingletonMultiplicitySelector<T> {
195    /// Test whether the value is `Default`
196    pub fn is_default(self, item: &T) -> bool
197    where
198        T: Default + Eq,
199    {
200        item == &Default::default()
201    }
202}
203
204/// Methods for handling optionality of a netdoc Object, during encoding
205///
206// This could be used for things other than Object, if there were any thing
207// that supported Option but not Vec.
208//
209/// **For use by macros**.
210///
211/// Each impl allows us to visit an optional element.
212pub trait OptionalityMethods: Copy + Sized {
213    /// The possibly-present element.
214    ///
215    /// Should match the corresponding
216    /// [`parse2::multiplicity::ObjectSetMethods::Each`].
217    /// (See [`MultiplicityMethods::Each`] for rationale.)
218    type Each: Sized + 'static;
219
220    /// The input type: the type of the field in the item struct.
221    type Field: Sized;
222
223    /// Yield the element, if there is one
224    fn as_option<'f>(self, f: &'f Self::Field) -> Option<&'f Self::Each>;
225}
226impl<T: 'static> OptionalityMethods for MultiplicitySelector<Option<T>> {
227    type Each = T;
228    type Field = Option<T>;
229    fn as_option<'f>(self, f: &'f Self::Field) -> Option<&'f Self::Each> {
230        f.as_ref()
231    }
232}
233impl<T: 'static> OptionalityMethods for &'_ MultiplicitySelector<T> {
234    type Each = T;
235    type Field = T;
236    fn as_option<'f>(self, f: &'f Self::Field) -> Option<&'f Self::Each> {
237        Some(f)
238    }
239}