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/// Helper type that allows us to select an impl of `MultiplicityMethods`
49///
50/// **For use by macros**.
51///
52/// This is distinct from `parse2::MultiplicitySelector`,
53/// principally because it has the opposite variance.
54#[derive(Educe)]
55#[educe(Clone, Copy, Default)]
56pub struct MultiplicitySelector<Field>(PhantomData<fn(Field)>);
57
58/// Helper type implementing `MultiplicityMethods`, after the multiplicity is determined
59///
60/// **For use by macros**.
61#[derive(Educe)]
62#[educe(Clone, Copy, Default)]
63pub struct DeterminedMultiplicitySelector<Field>(PhantomData<fn(Field)>);
64
65/// Helper type implementing `MultiplicityMethods`, when a field is statically a singleton
66///
67/// **For use by macros**.
68#[derive(Educe)]
69#[educe(Clone, Copy, Default)]
70pub struct SingletonMultiplicitySelector<Field>(PhantomData<fn(Field)>);
71
72/// Methods for handling some multiplicity of netdoc elements, during encoding
73///
74/// **For use by macros**.
75///
76/// Each multiplicity impl allows us to iterate over the element(s).
77///
78/// Methods are also provided for typechecking, which are used by the derive macro to
79/// produce reasonable error messages when a trait impl is missing.
80//
81// When adding features here, for example by implementing this trait,
82// update the documentation in the `NetdocEncodable` and `ItemValueEncodable` derives.
83pub trait MultiplicityMethods<'f>: Copy + Sized {
84 /// The value for each thing.
85 type Each: Sized + 'f;
86
87 /// The input type: the type of the field in the netdoc or item struct.
88 type Field: Sized;
89
90 /// Return the appropriate implementor of `MultiplicityMethods`
91 fn selector(self) -> Self {
92 self
93 }
94
95 /// Yield the items, in a stable order
96 fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> + 'f;
97
98 /// Cause a compiler error if the element is not `NetdocEncodable`
99 fn check_netdoc_encodable(self)
100 where
101 Self::Each: NetdocEncodable,
102 {
103 }
104 /// Cause a compiler error if the element is not `ItemValueEncodable`
105 fn check_item_value_encodable(self)
106 where
107 Self::Each: ItemValueEncodable,
108 {
109 }
110 /// Cause a compiler error if the element is not `ItemArgument`
111 fn check_item_argument_encodable(self)
112 where
113 Self::Each: ItemArgument,
114 {
115 }
116 /// Cause a compiler error if the element is not `ItemObjectEncodable`
117 fn check_item_object_encodable(self)
118 where
119 Self::Each: ItemObjectEncodable,
120 {
121 }
122}
123
124impl<T> MultiplicitySelector<Vec<T>> {
125 /// Return the appropriate implementor of `MultiplicityMethods`
126 ///
127 /// This is an inherent method so that it doesn't need the `EncodeOrd` bounds:
128 /// that way if `EncodeOrd` is not implemented, we get a message about that,
129 /// rather than a complaint that `ItemValueEncodable` isn't impl for `Vec<T>`.
130 pub fn selector(self) -> DeterminedMultiplicitySelector<Vec<T>> {
131 DeterminedMultiplicitySelector::default()
132 }
133}
134impl<'f, T: EncodeOrd + 'f> MultiplicityMethods<'f> for DeterminedMultiplicitySelector<Vec<T>> {
135 type Each = T;
136 type Field = Vec<T>;
137 fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
138 let mut v = f.iter().collect_vec();
139 v.sort_by(|a, b| a.encode_cmp(*b));
140 v.into_iter()
141 }
142}
143impl<'f, T: 'f> MultiplicityMethods<'f> for MultiplicitySelector<RetainedOrderVec<T>> {
144 type Each = T;
145 type Field = RetainedOrderVec<T>;
146 fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
147 f.0.iter()
148 }
149}
150impl<'f, T: 'f> MultiplicityMethods<'f> for MultiplicitySelector<BTreeSet<T>> {
151 type Each = T;
152 type Field = BTreeSet<T>;
153 fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
154 f.iter()
155 }
156}
157impl<'f, T: 'f> MultiplicityMethods<'f> for MultiplicitySelector<Option<T>> {
158 type Each = T;
159 type Field = Option<T>;
160 fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> + 'f {
161 f.iter()
162 }
163}
164impl<'f, T: 'f> MultiplicityMethods<'f> for &'_ MultiplicitySelector<T> {
165 type Each = T;
166 type Field = T;
167 fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> + 'f {
168 iter::once(f)
169 }
170}
171impl<'f, T: 'f> MultiplicityMethods<'f> for SingletonMultiplicitySelector<T> {
172 type Each = T;
173 type Field = T;
174 fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> + 'f {
175 iter::once(f)
176 }
177}
178
179/// Methods for handling optionality of a netdoc Object, during encoding
180///
181// This could be used for things other than Object, if there were any thing
182// that supported Option but not Vec.
183//
184/// **For use by macros**.
185///
186/// Each impl allows us to visit an optional element.
187pub trait OptionalityMethods: Copy + Sized {
188 /// The possibly-present element.
189 type Each: Sized + 'static;
190
191 /// The input type: the type of the field in the item struct.
192 type Field: Sized;
193
194 /// Yield the element, if there is one
195 fn as_option<'f>(self, f: &'f Self::Field) -> Option<&'f Self::Each>;
196}
197impl<T: 'static> OptionalityMethods for MultiplicitySelector<Option<T>> {
198 type Each = T;
199 type Field = Option<T>;
200 fn as_option<'f>(self, f: &'f Self::Field) -> Option<&'f Self::Each> {
201 f.as_ref()
202 }
203}
204impl<T: 'static> OptionalityMethods for &'_ MultiplicitySelector<T> {
205 type Each = T;
206 type Field = T;
207 fn as_option<'f>(self, f: &'f Self::Field) -> Option<&'f Self::Each> {
208 Some(f)
209 }
210}