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