tor_netdoc/parse2/multiplicity.rs
1//! Multiplicity of fields (Items and Arguments)
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 `encode::multiplicity` which is the corresponding module for encoding.
9//!
10//! # Explanation
11//!
12//! We use autoref specialisation to allow macros to dispatch to
13//! trait impls for `Vec<T: ItemValueParseable>`, `Option<T>` etc.
14//! as well as simply unadorned `T`.
15//!
16//! We implement traits on a helper type `struct `[`MultiplicitySelector<Field>`].
17//!
18//! For Items we have `trait `[`ItemSetMethods`].
19//!
20//! `ItemSetMethods` is implemented for `MultiplicitySelector<Field>`
21//! for each supported `Field`.
22//! So, for `MultiplicitySelector<T>`, `MultiplicitySelector<Option<T>>`, and `MultiplicitySelector<Vec<T>>`.
23//! *But*, for just `T`, the impl is on `&MultiplicitySelector<T>`.
24//!
25//! When methods on `MultiplicitySelector` are called, the compiler finds
26//! the specific implementation for `MultiplicitySelector<Option<_>>` or `..Vec<_>`,
27//! or, failing that, derefs and finds the blanket impl on `&MultiplicitySelector<T>`.
28//!
29//! For Arguments we have [`ArgumentSetMethods`],
30//! and for Objects, [`ObjectSetMethods`],
31//! which work similarly.
32//!
33//! (We need separate traits for each of the kinds of netdoc element,
34//! for good support of inference in the derive macro.
35//! Type inference is particularly difficult during parsing, since we need the type
36//! information to flow from the field type, which is the *destination*
37//! to which a value is going to be stored.)
38
39use super::*;
40
41/// Helper type that allows us to select an impl of `ItemSetMethods` etc.
42///
43/// **For use by macros**.
44///
45/// See the [module-level docs](multiplicity).
46///
47/// This is distinct from `encode::MultiplicitySelector`,
48/// principally because it has the opposite variance.
49#[derive(Educe)]
50#[educe(Clone, Copy, Default)]
51pub struct MultiplicitySelector<Field>(PhantomData<fn() -> Field>);
52
53/// Methods for handling some multiplicity of Items, during parsing
54///
55/// **For use by macros**.
56///
57/// During parsing, we accumulate into a value of type `Option<Self::Field>`.
58/// The semantics of this are item-set-implementation-dependent;
59/// using a type which is generic over the field type in a simple way
60/// allows the partially-parsed accumulation state for a whole netdoc to have a concrete type.
61///
62/// See the [module-level docs](multiplicity), and
63/// [Field type in `NetdocParseable`](derive_deftly_template_NetdocParseable#field-type).
64///
65/// # Example
66///
67/// The code in the (derive) macro output is roughly like this:
68///
69/// ```
70/// use tor_netdoc::parse2::multiplicity::{MultiplicitySelector, ItemSetMethods as _};
71///
72/// let selector = MultiplicitySelector::<Vec<i32>>::default();
73/// let mut accum = None;
74/// selector.accumulate(&mut accum, 12).unwrap();
75/// let out = selector.finish(accum, "item-set").unwrap();
76///
77/// assert_eq!(out, [12]);
78/// ```
79//
80// When implementing this, update the documentation in the `NetdocParseable` derive.
81pub trait ItemSetMethods: Copy + Sized {
82 /// The value for each Item.
83 type Each: Sized;
84
85 /// The output type: the type of the field in the netdoc struct.
86 type Field: Sized;
87
88 /// Can we accumulate another item ?
89 ///
90 /// Can be used to help predict whether `accumulate` will throw.
91 fn can_accumulate(self, acc: &Option<Self::Field>) -> Result<(), EP>;
92
93 /// Accumulate one value into the accumulator.
94 fn accumulate(self, acc: &mut Option<Self::Field>, one: Self::Each) -> Result<(), EP>;
95
96 /// Resolve the accumulator into the output.
97 fn finish(
98 self,
99 acc: Option<Self::Field>,
100 item_keyword: &'static str,
101 ) -> Result<Self::Field, EP>;
102
103 /// If the contained type is a sub-document, call its `is_intro_item_keyword`.
104 fn is_intro_item_keyword(self, kw: KeywordRef<'_>) -> bool
105 where
106 Self::Each: NetdocParseable,
107 {
108 Self::Each::is_intro_item_keyword(kw)
109 }
110
111 /// If the contained type is a sub-document, call its `is_structural_keyword`.
112 fn is_structural_keyword(self, kw: KeywordRef<'_>) -> Option<IsStructural>
113 where
114 Self::Each: NetdocParseable,
115 {
116 Self::Each::is_structural_keyword(kw)
117 }
118
119 /// `finish` for if the contained type is a wsub-document
120 ///
121 /// Obtain the sub-document's intro keyword from its `doctype_for_error`.
122 fn finish_subdoc(self, acc: Option<Self::Field>) -> Result<Self::Field, EP>
123 where
124 Self::Each: NetdocParseable,
125 {
126 self.finish(acc, Self::Each::doctype_for_error())
127 }
128
129 /// Check that the element type is an Item
130 ///
131 /// For providing better error messages when struct fields don't implement the right trait.
132 /// See `derive.rs`, and search for this method name.
133 fn check_item_value_parseable(self)
134 where
135 Self::Each: ItemValueParseable,
136 {
137 }
138 /// Check that the element type is a Signature
139 fn check_signature_item_parseable(self)
140 where
141 Self::Each: SignatureItemParseable,
142 {
143 }
144 /// Check that the element type is a sub-document
145 fn check_subdoc_parseable(self)
146 where
147 Self::Each: NetdocParseable,
148 {
149 }
150 /// Check that the element type is an argument
151 fn check_item_argument_parseable(self)
152 where
153 Self::Each: ItemArgumentParseable,
154 {
155 }
156}
157impl<T> ItemSetMethods for MultiplicitySelector<Vec<T>> {
158 type Each = T;
159 type Field = Vec<T>;
160 // We always have None, or Some(nonempty)
161 fn can_accumulate(self, _acc: &Option<Vec<T>>) -> Result<(), EP> {
162 Ok(())
163 }
164 fn accumulate(self, acc: &mut Option<Vec<T>>, item: T) -> Result<(), EP> {
165 acc.get_or_insert_default().push(item);
166 Ok(())
167 }
168 fn finish(self, acc: Option<Vec<T>>, _keyword: &'static str) -> Result<Vec<T>, EP> {
169 Ok(acc.unwrap_or_default())
170 }
171}
172impl<T: Ord> ItemSetMethods for MultiplicitySelector<BTreeSet<T>> {
173 type Each = T;
174 type Field = BTreeSet<T>;
175 // We always have None, or Some(nonempty)
176 fn can_accumulate(self, _acc: &Option<BTreeSet<T>>) -> Result<(), EP> {
177 Ok(())
178 }
179 fn accumulate(self, acc: &mut Option<BTreeSet<T>>, item: T) -> Result<(), EP> {
180 if !acc.get_or_insert_default().insert(item) {
181 return Err(EP::ItemRepeated);
182 }
183 Ok(())
184 }
185 fn finish(self, acc: Option<BTreeSet<T>>, _keyword: &'static str) -> Result<BTreeSet<T>, EP> {
186 Ok(acc.unwrap_or_default())
187 }
188}
189impl<T> ItemSetMethods for MultiplicitySelector<Option<T>> {
190 type Each = T;
191 type Field = Option<T>;
192 // We always have None, or Some(Some(_))
193 fn can_accumulate(self, acc: &Option<Option<T>>) -> Result<(), EP> {
194 if acc.is_some() {
195 Err(EP::ItemRepeated)?;
196 }
197 Ok(())
198 }
199 // We always have None, or Some(Some(_))
200 fn accumulate(self, acc: &mut Option<Option<T>>, item: T) -> Result<(), EP> {
201 self.can_accumulate(acc)?;
202 *acc = Some(Some(item));
203 Ok(())
204 }
205 fn finish(self, acc: Option<Option<T>>, _keyword: &'static str) -> Result<Option<T>, EP> {
206 Ok(acc.flatten())
207 }
208}
209impl<T> ItemSetMethods for &'_ MultiplicitySelector<T> {
210 type Each = T;
211 type Field = T;
212 fn can_accumulate(self, acc: &Option<T>) -> Result<(), EP> {
213 if acc.is_some() {
214 Err(EP::ItemRepeated)?;
215 }
216 Ok(())
217 }
218 fn accumulate(self, acc: &mut Option<T>, item: T) -> Result<(), EP> {
219 self.can_accumulate(acc)?;
220 *acc = Some(item);
221 Ok(())
222 }
223 fn finish(self, acc: Option<T>, keyword: &'static str) -> Result<T, EP> {
224 acc.ok_or(EP::MissingItem { keyword })
225 }
226}
227
228/// Method for handling some multiplicity of Arguments
229///
230/// **For use by macros**.
231///
232/// See the [module-level docs](multiplicity), and
233/// [Field type in `ItemValueParseable`](derive_deftly_template_ItemValueParseable#field-type).
234///
235/// # Example
236///
237/// The code in the (derive) macro output is roughly like this:
238///
239/// ```
240/// use tor_netdoc::parse2::multiplicity::{MultiplicitySelector, ArgumentSetMethods as _};
241/// use tor_netdoc::parse2::{ItemArgumentParseable, ItemStream, ParseInput};
242/// let doc = "intro-item 12 66\n";
243/// let input = ParseInput::new(doc, "<literal>");
244/// let mut items = ItemStream::new(&input).unwrap();
245/// let mut item = items.next().unwrap().unwrap();
246///
247/// let args = MultiplicitySelector::<Vec<i32>>::default()
248/// .parse_with(item.args_mut(), ItemArgumentParseable::from_args)
249/// .unwrap();
250/// assert_eq!(args, [12, 66]);
251/// ```
252//
253// When implementing this, update the documentation in the `ItemValueParseable` derive.
254pub trait ArgumentSetMethods: Copy + Sized {
255 /// The value for each Item.
256 type Each: Sized;
257
258 /// The output type: the type of the field in the Item struct.
259 ///
260 /// This is *not* the type of an individual netdoc argument;
261 /// that is not explicity represented in the trait.
262 type Field: Sized;
263
264 /// Parse zero or more argument(s) into `Self::Field`.
265 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
266 where
267 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>;
268
269 /// Check that the element type is an Argument
270 ///
271 /// For providing better error messages when struct fields don't implement the right trait.
272 /// See `derive.rs`, and search for this method name.
273 fn check_argument_value_parseable(self)
274 where
275 Self::Each: ItemArgumentParseable,
276 {
277 }
278}
279impl<T> ArgumentSetMethods for MultiplicitySelector<Vec<T>> {
280 type Each = T;
281 type Field = Vec<T>;
282 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
283 where
284 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
285 {
286 let mut acc = vec![];
287 while args.something_to_yield() {
288 acc.push(parser(args)?);
289 }
290 Ok(acc)
291 }
292}
293impl<T: Ord> ArgumentSetMethods for MultiplicitySelector<BTreeSet<T>> {
294 type Each = T;
295 type Field = BTreeSet<T>;
296 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
297 where
298 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
299 {
300 let mut acc = BTreeSet::new();
301 while args.something_to_yield() {
302 if !acc.insert(parser(args)?) {
303 return Err(AE::Invalid);
304 }
305 }
306 Ok(acc)
307 }
308}
309impl<T> ArgumentSetMethods for MultiplicitySelector<Option<T>> {
310 type Each = T;
311 type Field = Option<T>;
312 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
313 where
314 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
315 {
316 if !args.something_to_yield() {
317 return Ok(None);
318 }
319 Ok(Some(parser(args)?))
320 }
321}
322impl<T> ArgumentSetMethods for &MultiplicitySelector<T> {
323 type Each = T;
324 type Field = T;
325 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
326 where
327 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
328 {
329 parser(args)
330 }
331}
332
333/// Method for handling some multiplicity of Objects
334///
335/// **For use by macros**.
336///
337/// See the [module-level docs](multiplicity), and
338/// [Field type in `ItemValueParseable`](derive_deftly_template_ItemValueParseable#field-type).
339///
340/// # Example
341///
342/// The code in the (derive) macro output is roughly like this:
343///
344/// ```
345/// use tor_netdoc::parse2::multiplicity::{MultiplicitySelector, ObjectSetMethods as _};
346/// use tor_netdoc::parse2::{ItemStream, ParseInput};
347/// let doc = "intro-item\n-----BEGIN OBJECT-----\naGVsbG8=\n-----END OBJECT-----\n";
348/// let input = ParseInput::new(doc, "<literal>");
349/// let mut items = ItemStream::new(&input).unwrap();
350/// let mut item = items.next().unwrap().unwrap();
351///
352/// let selector = MultiplicitySelector::<Option<String>>::default();
353/// let obj = item.object().map(|obj| {
354/// let data = obj.decode_data().unwrap();
355/// String::from_utf8(data)
356/// }).transpose().unwrap();
357/// let obj = selector.resolve_option(obj).unwrap();
358/// assert_eq!(obj, Some("hello".to_owned()));
359/// ```
360pub trait ObjectSetMethods: Copy + Sized {
361 /// The value for each Item.
362 type Each: Sized;
363
364 /// The output type: the type of the field in the Item struct.
365 type Field: Sized;
366
367 /// Parse zero or more argument(s) into `Self::Field`.
368 fn resolve_option(self, found: Option<Self::Each>) -> Result<Self::Field, EP>;
369
370 /// If the contained type is `ItemObjectParseable`, call its `check_label`
371 fn check_label(self, label: &str) -> Result<(), EP>
372 where
373 Self::Each: ItemObjectParseable,
374 {
375 Self::Each::check_label(label)
376 }
377
378 /// Check that the contained type can be parsed as an object
379 fn check_object_parseable(self)
380 where
381 Self::Each: ItemObjectParseable,
382 {
383 }
384}
385impl<T> ObjectSetMethods for MultiplicitySelector<Option<T>> {
386 type Field = Option<T>;
387 type Each = T;
388 fn resolve_option(self, found: Option<Self::Each>) -> Result<Self::Field, EP> {
389 Ok(found)
390 }
391}
392impl<T> ObjectSetMethods for &MultiplicitySelector<T> {
393 type Field = T;
394 type Each = T;
395 fn resolve_option(self, found: Option<Self::Each>) -> Result<Self::Field, EP> {
396 found.ok_or(EP::MissingObject)
397 }
398}