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 /// Accumulate one value into the accumulator.
73 fn accumulate(self, acc: &mut Option<Self::Field>, one: Self::Each) -> Result<(), EP>;
74 /// Resolve the accumulator into the output.
75 fn finish(
76 self,
77 acc: Option<Self::Field>,
78 item_keyword: &'static str,
79 ) -> Result<Self::Field, EP>;
80
81 /// If the contained type is a sub-document, call its `is_intro_item_keyword`.
82 fn is_intro_item_keyword(self, kw: KeywordRef<'_>) -> bool
83 where
84 Self::Each: NetdocParseable,
85 {
86 Self::Each::is_intro_item_keyword(kw)
87 }
88
89 /// `finish` for if the contained type is a wsub-document
90 ///
91 /// Obtain the sub-document's intro keyword from its `doctype_for_error`.
92 fn finish_subdoc(self, acc: Option<Self::Field>) -> Result<Self::Field, EP>
93 where
94 Self::Each: NetdocParseable,
95 {
96 self.finish(acc, Self::Each::doctype_for_error())
97 }
98}
99impl<T> ItemSetMethods for ItemSetSelector<Vec<T>> {
100 type Each = T;
101 type Field = Vec<T>;
102 // We always have None, or Some(nonempty)
103 fn accumulate(self, acc: &mut Option<Vec<T>>, item: T) -> Result<(), EP> {
104 acc.get_or_insert_default().push(item);
105 Ok(())
106 }
107 fn finish(self, acc: Option<Vec<T>>, _keyword: &'static str) -> Result<Vec<T>, EP> {
108 Ok(acc.unwrap_or_default())
109 }
110}
111impl<T> ItemSetMethods for ItemSetSelector<Option<T>> {
112 type Each = T;
113 type Field = Option<T>;
114 // We always have None, or Some(Some(_))
115 fn accumulate(self, acc: &mut Option<Option<T>>, item: T) -> Result<(), EP> {
116 if acc.is_some() {
117 Err(EP::ItemRepeated)?;
118 }
119 *acc = Some(Some(item));
120 Ok(())
121 }
122 fn finish(self, acc: Option<Option<T>>, _keyword: &'static str) -> Result<Option<T>, EP> {
123 Ok(acc.flatten())
124 }
125}
126impl<T> ItemSetMethods for &'_ ItemSetSelector<T> {
127 type Each = T;
128 type Field = T;
129 fn accumulate(self, acc: &mut Option<T>, item: T) -> Result<(), EP> {
130 if acc.is_some() {
131 Err(EP::ItemRepeated)?;
132 }
133 *acc = Some(item);
134 Ok(())
135 }
136 fn finish(self, acc: Option<T>, keyword: &'static str) -> Result<T, EP> {
137 acc.ok_or(EP::MissingItem { keyword })
138 }
139}
140
141/// Helper type that allows us to select an impl of `ArgumentSetMethods`
142///
143/// **For use by macros**.
144///
145/// See the [module-level docs](multiplicity), and
146/// [Field type in `ItemValueParseable`](derive_deftly_template_ItemValueParseable#field-type).
147///
148/// # Example
149///
150/// The code in the (derive) macro output is roughly like this:
151///
152/// ```
153/// use tor_netdoc::parse2::multiplicity::{ArgumentSetSelector, ArgumentSetMethods as _};
154/// use tor_netdoc::parse2::ItemStream;
155/// let doc = "intro-item 12 66\n";
156/// let mut items = ItemStream::new(doc).unwrap();
157/// let mut item = items.next().unwrap().unwrap();
158///
159/// let args = ArgumentSetSelector::<Vec<i32>>::default()
160/// .parse(item.args_mut(), "number")
161/// .unwrap();
162/// assert_eq!(args, [12, 66]);
163/// ```
164#[derive(Educe)]
165#[educe(Clone, Copy, Default)]
166pub struct ArgumentSetSelector<Field>(PhantomData<fn() -> Field>);
167
168/// Method for handling some multiplicity of Arguments
169///
170/// **For use by macros**.
171///
172/// See [`ArgumentSetSelector`] and the [module-level docs](multiplicity).
173pub trait ArgumentSetMethods: Copy + Sized {
174 /// The output type: the type of the field in the Item struct.
175 ///
176 /// This is *not* the type of an individual netdoc argument;
177 /// that is not explicity represented in the trait.
178 type Field: Sized;
179
180 /// Parse zero or more argument(s) into `Self::Field`.
181 fn parse(self, args: &mut ArgumentStream<'_>, field: &'static str) -> Result<Self::Field, EP>;
182}
183impl<T: ItemArgumentParseable> ArgumentSetMethods for ArgumentSetSelector<Vec<T>> {
184 type Field = Vec<T>;
185 fn parse<'s>(self, args: &mut ArgumentStream<'s>, f: &'static str) -> Result<Vec<T>, EP> {
186 let mut acc = vec![];
187 while args.is_nonempty_after_trim_start() {
188 acc.push(T::from_args(args, f)?);
189 }
190 Ok(acc)
191 }
192}
193impl<T: ItemArgumentParseable> ArgumentSetMethods for ArgumentSetSelector<Option<T>> {
194 type Field = Option<T>;
195 fn parse<'s>(self, args: &mut ArgumentStream<'s>, f: &'static str) -> Result<Option<T>, EP> {
196 if !args.is_nonempty_after_trim_start() {
197 return Ok(None);
198 }
199 Ok(Some(T::from_args(args, f)?))
200 }
201}
202impl<T: ItemArgumentParseable> ArgumentSetMethods for &ArgumentSetSelector<T> {
203 type Field = T;
204 fn parse<'s>(self, args: &mut ArgumentStream<'s>, f: &'static str) -> Result<T, EP> {
205 T::from_args(args, f)
206 }
207}