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