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::*;
40use crate::types::RetainedOrderVec;
41
42#[cfg(doc)]
43use crate::encode;
44
45/// Helper type that allows us to select an impl of `ItemSetMethods` etc.
46///
47/// **For use by macros**.
48///
49/// See the [module-level docs](multiplicity).
50///
51/// This is distinct from `encode::MultiplicitySelector`,
52/// principally because it has the opposite variance.
53#[derive(Educe)]
54#[educe(Clone, Copy, Default)]
55pub struct MultiplicitySelector<Field>(PhantomData<fn() -> Field>);
56
57/// Helper type that allows us to implement Debug
58///
59/// Returned by [`ItemSetMethods::item_set_debug`] etc.,
60/// using information from [`ItemSetMethods::debug_core`] etc.
61#[derive(derive_more::Debug)]
62#[debug("{}={}", self.0, self.1)]
63#[allow(dead_code)] // yes, they are only read by the Debug impl - that's what they're for!
64struct DebugHelper(
65 /// scope, eg `items`
66 &'static str,
67 /// multiplicity type pattern, eg `Vec<_>` or `1`
68 &'static str,
69);
70
71/// Methods for handling some multiplicity of Items, during parsing
72///
73/// **For use by macros**.
74///
75/// During parsing, we accumulate into a value of type `Option<Self::Field>`.
76/// The semantics of this are item-set-implementation-dependent;
77/// using a type which is generic over the field type in a simple way
78/// allows the partially-parsed accumulation state for a whole netdoc to have a concrete type.
79///
80/// See the [module-level docs](multiplicity), and
81/// [Field type in `NetdocParseable`](derive_deftly_template_NetdocParseable#field-type).
82///
83/// # Example
84///
85/// The code in the (derive) macro output is roughly like this:
86///
87/// ```
88/// use tor_netdoc::parse2::multiplicity::{MultiplicitySelector, ItemSetMethods as _};
89///
90/// let selector = MultiplicitySelector::<Vec<i32>>::default();
91/// let mut accum = None;
92/// selector.accumulate(&mut accum, 12).unwrap();
93/// let out = selector.finish(accum, "item-set").unwrap();
94///
95/// assert_eq!(out, [12]);
96/// ```
97//
98// When implementing this, update the documentation in the `NetdocParseable` derive.
99pub trait ItemSetMethods: Copy + Sized {
100 /// The value for each Item.
101 ///
102 /// Should match the corresponding
103 /// [`encode::MultiplicityMethods::Each`].
104 /// (See docs there for rationale.)
105 type Each: Sized;
106
107 /// The output type: the type of the field in the netdoc struct.
108 type Field: Sized;
109
110 /// Can we accumulate another item ?
111 ///
112 /// Can be used to help predict whether `accumulate` will throw.
113 fn can_accumulate(self, acc: &Option<Self::Field>) -> Result<(), EP>;
114
115 /// Accumulate one value into the accumulator.
116 fn accumulate(self, acc: &mut Option<Self::Field>, one: Self::Each) -> Result<(), EP>;
117
118 /// Multiplicity representation for `#[deftly(netdoc(debug))]` output, core
119 ///
120 /// Should generally be in a form like `Vec<_>`.
121 ///
122 /// See also [`ItemSetMethods::item_set_debug`], which is what the derives call.
123 //
124 // This can't be a `Debug` supertrait, because that won't work
125 // with the `&'_ MultiplicitySelector<T>` impl.
126 fn debug_core(self) -> &'static str;
127
128 /// Multiplicity representation for `#[deftly(netdoc(debug))]` output
129 ///
130 /// This adds a bit framing and type-fu that allows the derive macro's
131 /// call to be as simple as possible.
132 ///
133 /// See also [`ItemSetMethods::debug_core`], which is what each multiplicity implements.
134 //
135 // dtrace!, which we use for debugging in the parser macros, doesn't print variable names,
136 // thinking things are probably obvious enough. But for the elector here we want to
137 // include `items=`.
138 fn item_set_debug(self) -> impl Debug {
139 DebugHelper("items", self.debug_core())
140 }
141
142 /// Resolve the accumulator into the output.
143 fn finish(
144 self,
145 acc: Option<Self::Field>,
146 item_keyword: &'static str,
147 ) -> Result<Self::Field, EP>;
148
149 /// If the contained type is a sub-document, call its `is_intro_item_keyword`.
150 fn is_intro_item_keyword(self, kw: KeywordRef<'_>) -> bool
151 where
152 Self::Each: NetdocParseable,
153 {
154 Self::Each::is_intro_item_keyword(kw)
155 }
156
157 /// If the contained type is a sub-document, call its `is_structural_keyword`.
158 fn is_structural_keyword(self, kw: KeywordRef<'_>) -> Option<IsStructural>
159 where
160 Self::Each: NetdocParseable,
161 {
162 Self::Each::is_structural_keyword(kw)
163 }
164
165 /// `finish` for if the contained type is a wsub-document
166 ///
167 /// Obtain the sub-document's intro keyword from its `doctype_for_error`.
168 fn finish_subdoc(self, acc: Option<Self::Field>) -> Result<Self::Field, EP>
169 where
170 Self::Each: NetdocParseable,
171 {
172 self.finish(acc, Self::Each::doctype_for_error())
173 }
174
175 /// Check that the element type is an Item
176 ///
177 /// For providing better error messages when struct fields don't implement the right trait.
178 /// See `derive.rs`, and search for this method name.
179 fn check_item_value_parseable(self)
180 where
181 Self::Each: ItemValueParseable,
182 {
183 }
184 /// Check that the element type is a Signature
185 fn check_signature_item_parseable<H>(self, _: &mut H)
186 where
187 Self::Each: SignatureItemParseable,
188 H: AsMut<<Self::Each as SignatureItemParseable>::HashAccu>,
189 {
190 }
191 /// Check that the element type is a sub-document
192 fn check_subdoc_parseable(self)
193 where
194 Self::Each: NetdocParseable,
195 {
196 }
197 /// Check that the element type is an argument
198 fn check_item_argument_parseable(self)
199 where
200 Self::Each: ItemArgumentParseable,
201 {
202 }
203}
204impl<T> ItemSetMethods for MultiplicitySelector<Vec<T>> {
205 type Each = T;
206 type Field = Vec<T>;
207 // We always have None, or Some(nonempty)
208 fn can_accumulate(self, _acc: &Option<Vec<T>>) -> Result<(), EP> {
209 Ok(())
210 }
211 fn accumulate(self, acc: &mut Option<Vec<T>>, item: T) -> Result<(), EP> {
212 acc.get_or_insert_default().push(item);
213 Ok(())
214 }
215 fn finish(self, acc: Option<Vec<T>>, _keyword: &'static str) -> Result<Vec<T>, EP> {
216 Ok(acc.unwrap_or_default())
217 }
218 fn debug_core(self) -> &'static str {
219 "Vec<_>"
220 }
221}
222impl<T> ItemSetMethods for MultiplicitySelector<RetainedOrderVec<T>> {
223 type Each = T;
224 type Field = RetainedOrderVec<T>;
225 // We always have None, or Some(nonempty)
226 fn can_accumulate(self, _acc: &Option<RetainedOrderVec<T>>) -> Result<(), EP> {
227 Ok(())
228 }
229 fn accumulate(self, acc: &mut Option<RetainedOrderVec<T>>, item: T) -> Result<(), EP> {
230 acc.get_or_insert_default().0.push(item);
231 Ok(())
232 }
233 fn finish(
234 self,
235 acc: Option<RetainedOrderVec<T>>,
236 _keyword: &'static str,
237 ) -> Result<RetainedOrderVec<T>, EP> {
238 Ok(acc.unwrap_or_default())
239 }
240 fn debug_core(self) -> &'static str {
241 "RetainedOrderVec<_>"
242 }
243}
244impl<T: Ord> ItemSetMethods for MultiplicitySelector<BTreeSet<T>> {
245 type Each = T;
246 type Field = BTreeSet<T>;
247 // We always have None, or Some(nonempty)
248 fn can_accumulate(self, _acc: &Option<BTreeSet<T>>) -> Result<(), EP> {
249 Ok(())
250 }
251 fn accumulate(self, acc: &mut Option<BTreeSet<T>>, item: T) -> Result<(), EP> {
252 if !acc.get_or_insert_default().insert(item) {
253 return Err(EP::ItemRepeated);
254 }
255 Ok(())
256 }
257 fn finish(self, acc: Option<BTreeSet<T>>, _keyword: &'static str) -> Result<BTreeSet<T>, EP> {
258 Ok(acc.unwrap_or_default())
259 }
260 fn debug_core(self) -> &'static str {
261 "BTreeSet<_>"
262 }
263}
264impl<T> ItemSetMethods for MultiplicitySelector<Option<T>> {
265 type Each = T;
266 type Field = Option<T>;
267 // We always have None, or Some(Some(_))
268 fn can_accumulate(self, acc: &Option<Option<T>>) -> Result<(), EP> {
269 if acc.is_some() {
270 Err(EP::ItemRepeated)?;
271 }
272 Ok(())
273 }
274 // We always have None, or Some(Some(_))
275 fn accumulate(self, acc: &mut Option<Option<T>>, item: T) -> Result<(), EP> {
276 self.can_accumulate(acc)?;
277 *acc = Some(Some(item));
278 Ok(())
279 }
280 fn finish(self, acc: Option<Option<T>>, _keyword: &'static str) -> Result<Option<T>, EP> {
281 Ok(acc.flatten())
282 }
283 fn debug_core(self) -> &'static str {
284 "Option<_>"
285 }
286}
287impl<T> ItemSetMethods for &'_ MultiplicitySelector<T> {
288 type Each = T;
289 type Field = T;
290 fn can_accumulate(self, acc: &Option<T>) -> Result<(), EP> {
291 if acc.is_some() {
292 Err(EP::ItemRepeated)?;
293 }
294 Ok(())
295 }
296 fn accumulate(self, acc: &mut Option<T>, item: T) -> Result<(), EP> {
297 self.can_accumulate(acc)?;
298 *acc = Some(item);
299 Ok(())
300 }
301 fn finish(self, acc: Option<T>, keyword: &'static str) -> Result<T, EP> {
302 acc.ok_or(EP::MissingItem { keyword })
303 }
304 fn debug_core(self) -> &'static str {
305 // This appears in #[deftly(netdoc(debug))] output for singleton fields.
306 // We probably don't want the macros' users to have to think about our
307 // autoref-specialisation. So we don't write anything about `&` here.
308 "1"
309 }
310}
311
312/// Method for handling some multiplicity of Arguments
313///
314/// **For use by macros**.
315///
316/// See the [module-level docs](multiplicity), and
317/// [Field type in `ItemValueParseable`](derive_deftly_template_ItemValueParseable#field-type).
318///
319/// # Example
320///
321/// The code in the (derive) macro output is roughly like this:
322///
323/// ```
324/// use tor_netdoc::parse2::multiplicity::{MultiplicitySelector, ArgumentSetMethods as _};
325/// use tor_netdoc::parse2::{ItemArgumentParseable, ItemStream, ParseInput};
326/// let doc = "intro-item 12 66\n";
327/// let input = ParseInput::new(doc, "<literal>");
328/// let mut items = ItemStream::new(&input).unwrap();
329/// let mut item = items.next().unwrap().unwrap();
330///
331/// let args = MultiplicitySelector::<Vec<i32>>::default()
332/// .parse_with(item.args_mut(), ItemArgumentParseable::from_args)
333/// .unwrap();
334/// assert_eq!(args, [12, 66]);
335/// ```
336//
337// When implementing this, update the documentation in the `ItemValueParseable` derive.
338pub trait ArgumentSetMethods: Copy + Sized {
339 /// The value for each Item.
340 ///
341 /// Should match the corresponding
342 /// [`encode::MultiplicityMethods::Each`].
343 /// (See docs there for rationale.)
344 type Each: Sized;
345
346 /// The output type: the type of the field in the Item struct.
347 ///
348 /// This is *not* the type of an individual netdoc argument;
349 /// that is not explicitly represented in the trait.
350 type Field: Sized;
351
352 /// Parse zero or more argument(s) into `Self::Field`.
353 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
354 where
355 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>;
356
357 /// Multiplicity representation for `#[deftly(netdoc(debug))]` output, core
358 ///
359 /// Should generally be in a form like `Vec<_>`.
360 ///
361 /// See [`ItemSetMethods::debug_core`] and [`ArgumentSetMethods::argument_set_debug`].
362 fn debug_core(self) -> &'static str;
363
364 /// Multiplicity representation for `#[deftly(netdoc(debug))]` output
365 ///
366 /// See [`ItemSetMethods::item_set_debug`] and [`ArgumentSetMethods::debug_core`].
367 fn argument_set_debug(self) -> impl Debug {
368 DebugHelper("args", self.debug_core())
369 }
370
371 /// Check that the element type is an Argument
372 ///
373 /// For providing better error messages when struct fields don't implement the right trait.
374 /// See `derive.rs`, and search for this method name.
375 fn check_argument_value_parseable(self)
376 where
377 Self::Each: ItemArgumentParseable,
378 {
379 }
380}
381impl<T> ArgumentSetMethods for MultiplicitySelector<Vec<T>> {
382 type Each = T;
383 type Field = Vec<T>;
384 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
385 where
386 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
387 {
388 let mut acc = vec![];
389 while args.something_to_yield() {
390 acc.push(parser(args)?);
391 }
392 Ok(acc)
393 }
394 fn debug_core(self) -> &'static str {
395 "Vec<_>"
396 }
397}
398impl<T: Ord> ArgumentSetMethods for MultiplicitySelector<BTreeSet<T>> {
399 type Each = T;
400 type Field = BTreeSet<T>;
401 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
402 where
403 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
404 {
405 let mut acc = BTreeSet::new();
406 while args.something_to_yield() {
407 if !acc.insert(parser(args)?) {
408 return Err(AE::Invalid);
409 }
410 }
411 Ok(acc)
412 }
413 fn debug_core(self) -> &'static str {
414 "BTreeSet<_>"
415 }
416}
417impl<T> ArgumentSetMethods for MultiplicitySelector<Option<T>> {
418 type Each = T;
419 type Field = Option<T>;
420 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
421 where
422 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
423 {
424 if !args.something_to_yield() {
425 return Ok(None);
426 }
427 Ok(Some(parser(args)?))
428 }
429 fn debug_core(self) -> &'static str {
430 "Option<_>"
431 }
432}
433impl<T> ArgumentSetMethods for &MultiplicitySelector<T> {
434 type Each = T;
435 type Field = T;
436 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
437 where
438 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
439 {
440 parser(args)
441 }
442 fn debug_core(self) -> &'static str {
443 "1"
444 }
445}
446
447/// Method for handling some multiplicity of Objects
448///
449/// **For use by macros**.
450///
451/// See the [module-level docs](multiplicity), and
452/// [Field type in `ItemValueParseable`](derive_deftly_template_ItemValueParseable#field-type).
453///
454/// # Example
455///
456/// The code in the (derive) macro output is roughly like this:
457///
458/// ```
459/// use tor_netdoc::parse2::multiplicity::{MultiplicitySelector, ObjectSetMethods as _};
460/// use tor_netdoc::parse2::{ItemStream, ParseInput};
461/// let doc = "intro-item\n-----BEGIN OBJECT-----\naGVsbG8=\n-----END OBJECT-----\n";
462/// let input = ParseInput::new(doc, "<literal>");
463/// let mut items = ItemStream::new(&input).unwrap();
464/// let mut item = items.next().unwrap().unwrap();
465///
466/// let selector = MultiplicitySelector::<Option<String>>::default();
467/// let obj = item.object().map(|obj| {
468/// let data = obj.decode_data().unwrap();
469/// String::from_utf8(data)
470/// }).transpose().unwrap();
471/// let obj = selector.resolve_option(obj).unwrap();
472/// assert_eq!(obj, Some("hello".to_owned()));
473/// ```
474pub trait ObjectSetMethods: Copy + Sized {
475 /// The value for each Item.
476 ///
477 /// Should match the corresponding
478 /// [`encode::OptionalityMethods::Each`].
479 /// (See [`encode::MultiplicityMethods::Each`] for rationale.)
480 type Each: Sized;
481
482 /// The output type: the type of the field in the Item struct.
483 type Field: Sized;
484
485 /// Parse zero or more argument(s) into `Self::Field`.
486 fn resolve_option(self, found: Option<Self::Each>) -> Result<Self::Field, EP>;
487
488 /// Multiplicity representation for `#[deftly(netdoc(debug))]` output, core
489 ///
490 /// Should generally be in a form like `Option<_>`.
491 ///
492 /// See [`ItemSetMethods::debug_core`] and [`ObjectSetMethods::object_set_debug`].
493 fn debug_core(self) -> &'static str;
494
495 /// Multiplicity representation for `#[deftly(netdoc(debug))]` output
496 ///
497 /// See [`ItemSetMethods::item_set_debug`] and [`ObjectSetMethods::debug_core`].
498 fn object_set_debug(self) -> impl Debug {
499 DebugHelper("object", self.debug_core())
500 }
501
502 /// If the contained type is `ItemObjectParseable`, call its `check_label`
503 fn check_label(self, label: &str) -> Result<(), EP>
504 where
505 Self::Each: ItemObjectParseable,
506 {
507 Self::Each::check_label(label)
508 }
509
510 /// Check that the contained type can be parsed as an object
511 fn check_object_parseable(self)
512 where
513 Self::Each: ItemObjectParseable,
514 {
515 }
516}
517impl<T> ObjectSetMethods for MultiplicitySelector<Option<T>> {
518 type Field = Option<T>;
519 type Each = T;
520 fn resolve_option(self, found: Option<Self::Each>) -> Result<Self::Field, EP> {
521 Ok(found)
522 }
523 fn debug_core(self) -> &'static str {
524 "Option<_>"
525 }
526}
527impl<T> ObjectSetMethods for &MultiplicitySelector<T> {
528 type Field = T;
529 type Each = T;
530 fn resolve_option(self, found: Option<Self::Each>) -> Result<Self::Field, EP> {
531 found.ok_or(EP::MissingObject)
532 }
533 fn debug_core(self) -> &'static str {
534 "1"
535 }
536}