aboutsummaryrefslogtreecommitdiff
path: root/crates/tor-netdoc/src/encode/multiplicity.rs
blob: f29f5e05ac54d9b7c38c1a0ef3c5e69efe488b6e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
//! 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<T>`, `Option<T>` etc. as well as simply unadorned `T`.
//!
//! When methods on `MultiplicitySelector` are called, the compiler finds
//! the specific implementation for `MultiplicitySelector<Option<_>>` or `..Vec<_>`,
//! or, failing that, derefs and finds the blanket impl on `&MultiplicitySelector<T>`.
//!
//! For Objects, where only `T` and `Option<T>` are allowed,
//! we use `OptionalityMethods`.
//!
//! We implement traits on helper types `struct `[`MultiplicitySelector<Field>`],
//! [`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<NotItemValueParseable>`,
//! 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::<Vec<_>>::default().selector()`](MultiplicitySelector::<Vec<T>>::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<Field>(PhantomData<fn(Field)>);

/// Helper type implementing `MultiplicityMethods`, after the multiplicity is determined
///
/// **For use by macros**.
#[derive(Educe)]
#[educe(Clone, Copy, Default)]
pub struct DeterminedMultiplicitySelector<Field>(PhantomData<fn(Field)>);

/// Helper type implementing `MultiplicityMethods`, when a field is statically a singleton
///
/// **For use by macros**.
#[derive(Educe)]
#[educe(Clone, Copy, Default)]
pub struct SingletonMultiplicitySelector<Field>(PhantomData<fn(Field)>);

/// 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<Item = &'f Self::Each> + '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<T> MultiplicitySelector<Vec<T>> {
    /// 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<T>`.
    pub fn selector(self) -> DeterminedMultiplicitySelector<Vec<T>> {
        DeterminedMultiplicitySelector::default()
    }
}
impl<'f, T: EncodeOrd + 'f> MultiplicityMethods<'f> for DeterminedMultiplicitySelector<Vec<T>> {
    type Each = T;
    type Field = Vec<T>;
    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
        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<RetainedOrderVec<T>> {
    type Each = T;
    type Field = RetainedOrderVec<T>;
    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
        f.0.iter()
    }
}
impl<'f, T: 'f> MultiplicityMethods<'f> for MultiplicitySelector<BTreeSet<T>> {
    type Each = T;
    type Field = BTreeSet<T>;
    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> {
        f.iter()
    }
}
impl<'f, T: 'f> MultiplicityMethods<'f> for MultiplicitySelector<Option<T>> {
    type Each = T;
    type Field = Option<T>;
    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> + 'f {
        f.iter()
    }
}
impl<'f, T: 'f> MultiplicityMethods<'f> for &'_ MultiplicitySelector<T> {
    type Each = T;
    type Field = T;
    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> + 'f {
        iter::once(f)
    }
}
impl<'f, T: 'f> MultiplicityMethods<'f> for SingletonMultiplicitySelector<T> {
    type Each = T;
    type Field = T;
    fn iter_ordered(self, f: &'f Self::Field) -> impl Iterator<Item = &'f Self::Each> + 'f {
        iter::once(f)
    }
}
impl<T> SingletonMultiplicitySelector<T> {
    /// 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<T: 'static> OptionalityMethods for MultiplicitySelector<Option<T>> {
    type Each = T;
    type Field = Option<T>;
    fn as_option<'f>(self, f: &'f Self::Field) -> Option<&'f Self::Each> {
        f.as_ref()
    }
}
impl<T: 'static> OptionalityMethods for &'_ MultiplicitySelector<T> {
    type Each = T;
    type Field = T;
    fn as_option<'f>(self, f: &'f Self::Field) -> Option<&'f Self::Each> {
        Some(f)
    }
}