tor_netdoc/parse2/traits.rs
1//! Core model for netdoc parsing
2
3use super::*;
4
5/// A document or section that can be parsed
6///
7/// Normally [derived](derive_deftly_template_NetdocParseable).
8pub trait NetdocParseable: Sized {
9 /// Document type for errors, normally its intro keyword
10 fn doctype_for_error() -> &'static str;
11
12 /// Is `Keyword` an intro Item Keyword for this kind of document?
13 ///
14 /// This is used with 1-keyword lookahead, to allow us to push or pop
15 /// the parsing state into or out of a sub-document.
16 ///
17 /// For signatures sections, this should report *every* recognised keyword.
18 fn is_intro_item_keyword(kw: KeywordRef<'_>) -> bool;
19
20 /// Parse the document from a stream of Items
21 ///
22 /// Should stop before reading any keyword matching `stop_at`.
23 /// (Except, right at the start.)
24 ///
25 /// Should also stop before reading a 2nd intro keyword,
26 /// so that successive calls to this function can parse
27 /// successive sub-documents of this kind.
28 ///
29 /// Otherwise, should continue until EOF.
30 ///
31 /// Must check whether the first item is this document's `is_intro_item_keyword`,
32 /// and error if not.
33 fn from_items(input: &mut ItemStream<'_>, stop_at: stop_at!()) -> Result<Self, ErrorProblem>;
34}
35
36/// A collection of fields that can be parsed within a section
37///
38/// None of the items can be structural.
39///
40/// Normally [derived](derive_deftly_template_NetdocParseableFields).
41pub trait NetdocParseableFields: Sized {
42 /// The partially-parsed set of items.
43 type Accumulator: Sized + Debug + Send + Sync + 'static;
44
45 /// Is this one of the keywords in this struct
46 fn is_item_keyword(kw: KeywordRef<'_>) -> bool;
47
48 /// Accumulate an item in this struct
49 ///
50 /// # Panics
51 ///
52 /// The caller must have first checked the `item`'s keyword with `is_item_keyword`.
53 /// If this *isn't* an item for this structure, may panic.
54 fn accumulate_item(acc: &mut Self::Accumulator, item: UnparsedItem<'_>) -> Result<(), EP>;
55
56 /// Finish
57 ///
58 /// Resolves the `Accumulator` into the output type.
59 /// Generally, this means throwing an error if expected fields were not present.
60 fn finish(acc: Self::Accumulator) -> Result<Self, EP>;
61}
62
63/// A network document with (unverified) signatures
64///
65/// Typically implemented automatically, for `FooSigned` structs, as defined by
66/// [`#[derive_deftly(NetdocSigned)]`](derive_deftly_template_NetdocSigned).
67//
68// TODO is this only useable for parsing? It needs to be renamed, or maybe impooved and moved
69pub trait NetdocSigned {
70 /// The body, ie not including the signatures
71 type Body: Sized;
72 /// The signatures (the whole signature section)
73 type Signatures: Sized;
74
75 /// Inspect the document (and its signatures)
76 ///
77 /// # Security hazard
78 ///
79 /// The signature has not been verified, so the returned data must not be trusted.
80 fn inspect_unverified(&self) -> (&Self::Body, &Self::Signatures);
81
82 /// Obtain the actual document (and signatures), without verifying
83 ///
84 /// # Security hazard
85 ///
86 /// The signature has not been verified, so the returned data must not be trusted.
87 fn unwrap_unverified(self) -> (Self::Body, Self::Signatures);
88
89 /// Construct a new `NetdocSigned` from a body and signatures
90 ///
91 /// (Called by code generated by `#[derive_deftly(NetdocSigned)]`.)
92 fn from_parts(body: Self::Body, signatures: Self::Signatures) -> Self;
93}
94
95/// An item (value) that can be parsed in a netdoc
96///
97/// This is the type `T` of a field `item: T` in a netdoc type.
98///
99/// An implementation is provided for tuples of `ItemArgumentParseable`,
100/// which parses each argument in turn,
101/// ignores additional arguments,
102/// and rejects any Object.
103///
104/// Typically derived with
105/// [`#[derive_deftly(ItemValueParseable)]`](derive_deftly_template_ItemValueParseable).
106///
107/// Signature items are special, and implement [`SignatureItemParseable`] instead.
108pub trait ItemValueParseable: Sized {
109 /// Parse the item's value
110 fn from_unparsed(item: UnparsedItem<'_>) -> Result<Self, ErrorProblem>;
111}
112
113/// An (individual) argument that can be parsed from in a netdoc
114///
115/// An implementations is provided for **`T: FromStr`**,
116/// which expects a single argument and passes it to `FromStr`.
117///
118/// For netdoc arguments whose specified syntax spans multiple space-separated words,
119/// use a manual implementation or a wrapper type.
120pub trait ItemArgumentParseable: Sized {
121 /// Parse the argument
122 fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<Self, ArgumentError>;
123}
124
125/// An Object value that be parsed from a netdoc
126pub trait ItemObjectParseable: Sized {
127 /// Check that the Label is right
128 fn check_label(label: &str) -> Result<(), ErrorProblem>;
129
130 /// Convert the bytes of the Object (which was present) into the actual value
131 ///
132 /// `input` has been base64-decoded.
133 fn from_bytes(input: &[u8]) -> Result<Self, ErrorProblem>;
134}
135
136//---------- provided blanket impls ----------
137
138impl<T: NormalItemArgument> ItemArgumentParseable for T {
139 fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<Self, AE> {
140 let v = args
141 .next()
142 .ok_or(AE::Missing)?
143 .parse()
144 .map_err(|_e| AE::Missing)?;
145 Ok(v)
146 }
147}
148
149impl<T: ItemValueParseable> ItemValueParseable for Arc<T> {
150 fn from_unparsed(item: UnparsedItem<'_>) -> Result<Self, ErrorProblem> {
151 T::from_unparsed(item).map(Arc::new)
152 }
153}
154
155impl<T: NetdocParseable> NetdocParseable for Arc<T> {
156 fn doctype_for_error() -> &'static str {
157 T::doctype_for_error()
158 }
159 fn is_intro_item_keyword(kw: KeywordRef<'_>) -> bool {
160 T::is_intro_item_keyword(kw)
161 }
162 fn from_items(input: &mut ItemStream<'_>, stop_at: stop_at!()) -> Result<Self, EP> {
163 T::from_items(input, stop_at).map(Arc::new)
164 }
165}
166impl<T: NetdocParseableFields> NetdocParseableFields for Arc<T> {
167 type Accumulator = T::Accumulator;
168 fn is_item_keyword(kw: KeywordRef<'_>) -> bool {
169 T::is_item_keyword(kw)
170 }
171 fn accumulate_item(acc: &mut Self::Accumulator, item: UnparsedItem<'_>) -> Result<(), EP> {
172 T::accumulate_item(acc, item)
173 }
174 fn finish(acc: Self::Accumulator) -> Result<Self, EP> {
175 T::finish(acc).map(Arc::new)
176 }
177}
178
179/// implement [`ItemValueParseable`] for a particular tuple size
180macro_rules! item_value_parseable_for_tuple {
181 { $($i:literal)* } => { paste! {
182 impl< $( [<T$i>]: ItemArgumentParseable, )* >
183 ItemValueParseable for ( $( [<T$i>], )* )
184 {
185 fn from_unparsed(
186 #[allow(unused_mut)]
187 mut item: UnparsedItem<'_>,
188 ) -> Result<Self, ErrorProblem> {
189 let r = ( $(
190 <[<T$i>] as ItemArgumentParseable>::from_args(
191 item.args_mut(),
192 ).map_err(item.args().error_handler(stringify!($i)))?,
193 )* );
194 item.check_no_object()?;
195 Ok(r)
196 }
197 }
198 } }
199}
200
201item_value_parseable_for_tuple! {}
202item_value_parseable_for_tuple! { 0 }
203item_value_parseable_for_tuple! { 0 1 }
204item_value_parseable_for_tuple! { 0 1 2 }
205item_value_parseable_for_tuple! { 0 1 2 3 }
206item_value_parseable_for_tuple! { 0 1 2 3 4 }
207item_value_parseable_for_tuple! { 0 1 2 3 4 5 }
208item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 }
209item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 }
210item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 8 }
211item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 8 9 }