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).
67pub trait NetdocSigned {
68    /// The body, ie not including the signatures
69    type Body: Sized;
70    /// The signatures (the whole signature section)
71    type Signatures: Sized;
72
73    /// Inspect the document (and its signatures)
74    ///
75    /// # Security hazard
76    ///
77    /// The signature has not been verified, so the returned data must not be trusted.
78    fn inspect_unverified(&self) -> (&Self::Body, &Self::Signatures);
79
80    /// Obtain the actual document (and signatures), without verifying
81    ///
82    /// # Security hazard
83    ///
84    /// The signature has not been verified, so the returned data must not be trusted.
85    fn unwrap_unverified(self) -> (Self::Body, Self::Signatures);
86
87    /// Construct a new `NetdocSigned` from a body and signatures
88    ///
89    /// (Called by code generated by `#[derive_deftly(NetdocSigned)]`.)
90    fn from_parts(body: Self::Body, signatures: Self::Signatures) -> Self;
91}
92
93/// An item (value) that can be parsed in a netdoc
94///
95/// This is the type `T` of a field `item: T` in a netdoc type.
96///
97/// An implementation is provided for tuples of `ItemArgumentParseable`,
98/// which parses each argument in turn,
99/// ignores additional arguments,
100/// and rejects any Object.
101///
102/// Typically derived with
103/// [`#[derive_deftly(ItemValueParseable)]`](derive_deftly_template_ItemValueParseable).
104///
105/// Signature items are special, and implement [`SignatureItemParseable`] instead.
106pub trait ItemValueParseable: Sized {
107    /// Parse the item's value
108    fn from_unparsed(item: UnparsedItem<'_>) -> Result<Self, ErrorProblem>;
109}
110
111/// An (individual) argument that can be parsed from in a netdoc
112///
113/// An implementations is provided for **`T: FromStr`**,
114/// which expects a single argument and passes it to `FromStr`.
115///
116/// For netdoc arguments whose specified syntax spans multiple space-separated words,
117/// use a manual implementation or a wrapper type.
118pub trait ItemArgumentParseable: Sized {
119    /// Parse the argument
120    fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<Self, ArgumentError>;
121}
122
123/// An Object value that be parsed from a netdoc
124pub trait ItemObjectParseable: Sized {
125    /// Check that the Label is right
126    fn check_label(label: &str) -> Result<(), ErrorProblem>;
127
128    /// Convert the bytes of the Object (which was present) into the actual value
129    ///
130    /// `input` has been base64-decoded.
131    fn from_bytes(input: &[u8]) -> Result<Self, ErrorProblem>;
132}
133
134//---------- provided blanket impls ----------
135
136impl<T: NormalItemArgument> ItemArgumentParseable for T {
137    fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<Self, AE> {
138        let v = args
139            .next()
140            .ok_or(AE::Missing)?
141            .parse()
142            .map_err(|_e| AE::Missing)?;
143        Ok(v)
144    }
145}
146
147impl<T: ItemValueParseable> ItemValueParseable for Arc<T> {
148    fn from_unparsed(item: UnparsedItem<'_>) -> Result<Self, ErrorProblem> {
149        T::from_unparsed(item).map(Arc::new)
150    }
151}
152
153impl<T: NetdocParseable> NetdocParseable for Arc<T> {
154    fn doctype_for_error() -> &'static str {
155        T::doctype_for_error()
156    }
157    fn is_intro_item_keyword(kw: KeywordRef<'_>) -> bool {
158        T::is_intro_item_keyword(kw)
159    }
160    fn from_items(input: &mut ItemStream<'_>, stop_at: stop_at!()) -> Result<Self, EP> {
161        T::from_items(input, stop_at).map(Arc::new)
162    }
163}
164impl<T: NetdocParseableFields> NetdocParseableFields for Arc<T> {
165    type Accumulator = T::Accumulator;
166    fn is_item_keyword(kw: KeywordRef<'_>) -> bool {
167        T::is_item_keyword(kw)
168    }
169    fn accumulate_item(acc: &mut Self::Accumulator, item: UnparsedItem<'_>) -> Result<(), EP> {
170        T::accumulate_item(acc, item)
171    }
172    fn finish(acc: Self::Accumulator) -> Result<Self, EP> {
173        T::finish(acc).map(Arc::new)
174    }
175}
176
177/// implement [`ItemValueParseable`] for a particular tuple size
178macro_rules! item_value_parseable_for_tuple {
179    { $($i:literal)* } => { paste! {
180        impl< $( [<T$i>]: ItemArgumentParseable, )* >
181            ItemValueParseable for ( $( [<T$i>], )* )
182        {
183            fn from_unparsed(
184                #[allow(unused_mut)]
185                mut item: UnparsedItem<'_>,
186            ) -> Result<Self, ErrorProblem> {
187                let r = ( $(
188                    <[<T$i>] as ItemArgumentParseable>::from_args(
189                        item.args_mut(),
190                    ).map_err(item.args().error_handler(stringify!($i)))?,
191                )* );
192                item.check_no_object()?;
193                Ok(r)
194            }
195        }
196    } }
197}
198
199item_value_parseable_for_tuple! {}
200item_value_parseable_for_tuple! { 0 }
201item_value_parseable_for_tuple! { 0 1 }
202item_value_parseable_for_tuple! { 0 1 2 }
203item_value_parseable_for_tuple! { 0 1 2 3 }
204item_value_parseable_for_tuple! { 0 1 2 3 4 }
205item_value_parseable_for_tuple! { 0 1 2 3 4 5 }
206item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 }
207item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 }
208item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 8 }
209item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 8 9 }