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