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