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 /// `finish` for if the contained type is a wsub-document
112 ///
113 /// Obtain the sub-document's intro keyword from its `doctype_for_error`.
114 fn finish_subdoc(self, acc: Option<Self::Field>) -> Result<Self::Field, EP>
115 where
116 Self::Each: NetdocParseable,
117 {
118 self.finish(acc, Self::Each::doctype_for_error())
119 }
120
121 /// Check that the element type is an Item
122 ///
123 /// For providing better error messages when struct fields don't implement the right trait.
124 /// See `derive.rs`, and search for this method name.
125 fn check_item_value_parseable(self)
126 where
127 Self::Each: ItemValueParseable,
128 {
129 }
130 /// Check that the element type is a Signature
131 fn check_signature_item_parseable(self)
132 where
133 Self::Each: SignatureItemParseable,
134 {
135 }
136 /// Check that the element type is a sub-document
137 fn check_subdoc_parseable(self)
138 where
139 Self::Each: NetdocParseable,
140 {
141 }
142 /// Check that the element type is an argument
143 fn check_item_argument_parseable(self)
144 where
145 Self::Each: ItemArgumentParseable,
146 {
147 }
148}
149impl<T> ItemSetMethods for MultiplicitySelector<Vec<T>> {
150 type Each = T;
151 type Field = Vec<T>;
152 // We always have None, or Some(nonempty)
153 fn can_accumulate(self, _acc: &Option<Vec<T>>) -> Result<(), EP> {
154 Ok(())
155 }
156 fn accumulate(self, acc: &mut Option<Vec<T>>, item: T) -> Result<(), EP> {
157 acc.get_or_insert_default().push(item);
158 Ok(())
159 }
160 fn finish(self, acc: Option<Vec<T>>, _keyword: &'static str) -> Result<Vec<T>, EP> {
161 Ok(acc.unwrap_or_default())
162 }
163}
164impl<T: Ord> ItemSetMethods for MultiplicitySelector<BTreeSet<T>> {
165 type Each = T;
166 type Field = BTreeSet<T>;
167 // We always have None, or Some(nonempty)
168 fn can_accumulate(self, _acc: &Option<BTreeSet<T>>) -> Result<(), EP> {
169 Ok(())
170 }
171 fn accumulate(self, acc: &mut Option<BTreeSet<T>>, item: T) -> Result<(), EP> {
172 if !acc.get_or_insert_default().insert(item) {
173 return Err(EP::ItemRepeated);
174 }
175 Ok(())
176 }
177 fn finish(self, acc: Option<BTreeSet<T>>, _keyword: &'static str) -> Result<BTreeSet<T>, EP> {
178 Ok(acc.unwrap_or_default())
179 }
180}
181impl<T> ItemSetMethods for MultiplicitySelector<Option<T>> {
182 type Each = T;
183 type Field = Option<T>;
184 // We always have None, or Some(Some(_))
185 fn can_accumulate(self, acc: &Option<Option<T>>) -> Result<(), EP> {
186 if acc.is_some() {
187 Err(EP::ItemRepeated)?;
188 }
189 Ok(())
190 }
191 // We always have None, or Some(Some(_))
192 fn accumulate(self, acc: &mut Option<Option<T>>, item: T) -> Result<(), EP> {
193 self.can_accumulate(acc)?;
194 *acc = Some(Some(item));
195 Ok(())
196 }
197 fn finish(self, acc: Option<Option<T>>, _keyword: &'static str) -> Result<Option<T>, EP> {
198 Ok(acc.flatten())
199 }
200}
201impl<T> ItemSetMethods for &'_ MultiplicitySelector<T> {
202 type Each = T;
203 type Field = T;
204 fn can_accumulate(self, acc: &Option<T>) -> Result<(), EP> {
205 if acc.is_some() {
206 Err(EP::ItemRepeated)?;
207 }
208 Ok(())
209 }
210 fn accumulate(self, acc: &mut Option<T>, item: T) -> Result<(), EP> {
211 self.can_accumulate(acc)?;
212 *acc = Some(item);
213 Ok(())
214 }
215 fn finish(self, acc: Option<T>, keyword: &'static str) -> Result<T, EP> {
216 acc.ok_or(EP::MissingItem { keyword })
217 }
218}
219
220/// Method for handling some multiplicity of Arguments
221///
222/// **For use by macros**.
223///
224/// See the [module-level docs](multiplicity), and
225/// [Field type in `ItemValueParseable`](derive_deftly_template_ItemValueParseable#field-type).
226///
227/// # Example
228///
229/// The code in the (derive) macro output is roughly like this:
230///
231/// ```
232/// use tor_netdoc::parse2::multiplicity::{MultiplicitySelector, ArgumentSetMethods as _};
233/// use tor_netdoc::parse2::{ItemArgumentParseable, ItemStream, ParseInput};
234/// let doc = "intro-item 12 66\n";
235/// let input = ParseInput::new(doc, "<literal>");
236/// let mut items = ItemStream::new(&input).unwrap();
237/// let mut item = items.next().unwrap().unwrap();
238///
239/// let args = MultiplicitySelector::<Vec<i32>>::default()
240/// .parse_with(item.args_mut(), ItemArgumentParseable::from_args)
241/// .unwrap();
242/// assert_eq!(args, [12, 66]);
243/// ```
244//
245// When implementing this, update the documentation in the `ItemValueParseable` derive.
246pub trait ArgumentSetMethods: Copy + Sized {
247 /// The value for each Item.
248 type Each: Sized;
249
250 /// The output type: the type of the field in the Item struct.
251 ///
252 /// This is *not* the type of an individual netdoc argument;
253 /// that is not explicity represented in the trait.
254 type Field: Sized;
255
256 /// Parse zero or more argument(s) into `Self::Field`.
257 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
258 where
259 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>;
260
261 /// Check that the element type is an Argument
262 ///
263 /// For providing better error messages when struct fields don't implement the right trait.
264 /// See `derive.rs`, and search for this method name.
265 fn check_argument_value_parseable(self)
266 where
267 Self::Each: ItemArgumentParseable,
268 {
269 }
270}
271impl<T> ArgumentSetMethods for MultiplicitySelector<Vec<T>> {
272 type Each = T;
273 type Field = Vec<T>;
274 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
275 where
276 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
277 {
278 let mut acc = vec![];
279 while args.something_to_yield() {
280 acc.push(parser(args)?);
281 }
282 Ok(acc)
283 }
284}
285impl<T: Ord> ArgumentSetMethods for MultiplicitySelector<BTreeSet<T>> {
286 type Each = T;
287 type Field = BTreeSet<T>;
288 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
289 where
290 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
291 {
292 let mut acc = BTreeSet::new();
293 while args.something_to_yield() {
294 if !acc.insert(parser(args)?) {
295 return Err(AE::Invalid);
296 }
297 }
298 Ok(acc)
299 }
300}
301impl<T> ArgumentSetMethods for MultiplicitySelector<Option<T>> {
302 type Each = T;
303 type Field = Option<T>;
304 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
305 where
306 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
307 {
308 if !args.something_to_yield() {
309 return Ok(None);
310 }
311 Ok(Some(parser(args)?))
312 }
313}
314impl<T> ArgumentSetMethods for &MultiplicitySelector<T> {
315 type Each = T;
316 type Field = T;
317 fn parse_with<P>(self, args: &mut ArgumentStream<'_>, parser: P) -> Result<Self::Field, AE>
318 where
319 P: for<'s> Fn(&mut ArgumentStream<'s>) -> Result<Self::Each, AE>,
320 {
321 parser(args)
322 }
323}
324
325/// Method for handling some multiplicity of Objects
326///
327/// **For use by macros**.
328///
329/// See the [module-level docs](multiplicity), and
330/// [Field type in `ItemValueParseable`](derive_deftly_template_ItemValueParseable#field-type).
331///
332/// # Example
333///
334/// The code in the (derive) macro output is roughly like this:
335///
336/// ```
337/// use tor_netdoc::parse2::multiplicity::{MultiplicitySelector, ObjectSetMethods as _};
338/// use tor_netdoc::parse2::{ItemStream, ParseInput};
339/// let doc = "intro-item\n-----BEGIN OBJECT-----\naGVsbG8=\n-----END OBJECT-----\n";
340/// let input = ParseInput::new(doc, "<literal>");
341/// let mut items = ItemStream::new(&input).unwrap();
342/// let mut item = items.next().unwrap().unwrap();
343///
344/// let selector = MultiplicitySelector::<Option<String>>::default();
345/// let obj = item.object().map(|obj| {
346/// let data = obj.decode_data().unwrap();
347/// String::from_utf8(data)
348/// }).transpose().unwrap();
349/// let obj = selector.resolve_option(obj).unwrap();
350/// assert_eq!(obj, Some("hello".to_owned()));
351/// ```
352pub trait ObjectSetMethods: Copy + Sized {
353 /// The value for each Item.
354 type Each: Sized;
355
356 /// The output type: the type of the field in the Item struct.
357 type Field: Sized;
358
359 /// Parse zero or more argument(s) into `Self::Field`.
360 fn resolve_option(self, found: Option<Self::Each>) -> Result<Self::Field, EP>;
361
362 /// If the contained type is `ItemObjectParseable`, call its `check_label`
363 fn check_label(self, label: &str) -> Result<(), EP>
364 where
365 Self::Each: ItemObjectParseable,
366 {
367 Self::Each::check_label(label)
368 }
369
370 /// Check that the contained type can be parsed as an object
371 fn check_object_parseable(self)
372 where
373 Self::Each: ItemObjectParseable,
374 {
375 }
376}
377impl<T> ObjectSetMethods for MultiplicitySelector<Option<T>> {
378 type Field = Option<T>;
379 type Each = T;
380 fn resolve_option(self, found: Option<Self::Each>) -> Result<Self::Field, EP> {
381 Ok(found)
382 }
383}
384impl<T> ObjectSetMethods for &MultiplicitySelector<T> {
385 type Field = T;
386 type Each = T;
387 fn resolve_option(self, found: Option<Self::Each>) -> Result<Self::Field, EP> {
388 found.ok_or(EP::MissingObject)
389 }
390}