1
//! Core model for netdoc parsing
2

            
3
use super::*;
4

            
5
/// A document or section that can be parsed
6
///
7
/// Normally [derived](derive_deftly_template_NetdocParseable).
8
pub 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
    /// Is `Keyword` a structural keyword for this kind of document?
36
    ///
37
    /// Returns `Some(IsStructural)` for:
38
    ///   - this type's intro item keyword (`is_intro_item_keyword`)
39
    ///   - the intro items or structural items for any of its sub-documents and sections
40
    ///     `#[deftly(netdoc(subdoc))]`
41
    ///
42
    /// (This means it returns true for *any* item in a signatures subdocument
43
    /// ie any field in a struct decorated `#[deftly(netdoc(signatures))]`
44
    /// since those are considered intro items.)
45
    ///
46
    /// Used for avoiding parsing ambiguity when a netdoc from a semi-trusted source
47
    /// is embedded into another netdoc.
48
    /// See <https://spec.torproject.org/dir-spec/creating-key-certificates.html#nesting>.
49
    ///
50
    /// # Return type and relationship to `is_intro_item_keyword`
51
    ///
52
    /// Returns `Option<IsStructural>`
53
    /// so that it has a different type to [`NetdocParseable::is_intro_item_keyword`],
54
    /// preventing accidental confusion between the two kinds of keyword property enquiry.
55
    ///
56
    /// Our parsing algorithms actually only care about *intro keywords* for sub-documents.
57
    /// We don't need to worry about anything else;
58
    /// notably, we don't need to care about other structural items within those sub-documents.
59
    ///
60
    /// Except for authcerts in votes,, which are nested documents
61
    /// with partially trusted content.
62
    /// That is what this method is for.
63
    ///
64
    /// So, we privilege `is_intro_item_keyword` by having it return `bool`
65
    /// and by the affordances in [`StopAt`].
66
    fn is_structural_keyword(kw: KeywordRef<'_>) -> Option<IsStructural>;
67
}
68

            
69
/// A collection of fields that can be parsed within a section
70
///
71
/// None of the items can be structural.
72
///
73
/// Normally [derived](derive_deftly_template_NetdocParseableFields).
74
pub trait NetdocParseableFields: Sized {
75
    /// The partially-parsed set of items.
76
    type Accumulator: Sized + Debug + Send + Sync + 'static;
77

            
78
    /// Is this one of the keywords in this struct
79
    fn is_item_keyword(kw: KeywordRef<'_>) -> bool;
80

            
81
    /// Accumulate an item in this struct
82
    ///
83
    /// # Panics
84
    ///
85
    /// The caller must have first checked the `item`'s keyword with `is_item_keyword`.
86
    /// If this *isn't* an item for this structure, may panic.
87
    fn accumulate_item(acc: &mut Self::Accumulator, item: UnparsedItem<'_>) -> Result<(), EP>;
88

            
89
    /// Finish
90
    ///
91
    /// Resolves the `Accumulator` into the output type.
92
    /// Generally, this means throwing an error if expected fields were not present.
93
    ///
94
    /// `items` is provided to obtain the parsing options and similar purposes.
95
    /// Its position in the input stream should not be relied on.
96
    fn finish(acc: Self::Accumulator, items: &ItemStream<'_>) -> Result<Self, EP>;
97
}
98

            
99
/// An item (value) that can be parsed in a netdoc
100
///
101
/// This is the type `T` of a field `item: T` in a netdoc type.
102
///
103
/// An implementation is provided for tuples of `ItemArgumentParseable`,
104
/// which parses each argument in turn,
105
/// ignores additional arguments,
106
/// and rejects any Object.
107
///
108
/// Typically derived with
109
/// [`#[derive_deftly(ItemValueParseable)]`](derive_deftly_template_ItemValueParseable).
110
///
111
/// Signature items are special, and implement [`SignatureItemParseable`] instead.
112
pub trait ItemValueParseable: Sized {
113
    /// Parse the item's value
114
    fn from_unparsed(item: UnparsedItem<'_>) -> Result<Self, ErrorProblem>;
115
}
116

            
117
/// An (individual) argument that can be parsed from in a netdoc
118
///
119
/// An implementations is provided for **`T: FromStr`**,
120
/// which expects a single argument and passes it to `FromStr`.
121
///
122
/// For netdoc arguments whose specified syntax spans multiple space-separated words,
123
/// use a manual implementation or a wrapper type.
124
pub trait ItemArgumentParseable: Sized {
125
    /// Parse the argument
126
    fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<Self, ArgumentError>;
127
}
128

            
129
/// An Object value that be parsed from a netdoc
130
pub trait ItemObjectParseable: Sized {
131
    /// Check that the Label is right
132
    fn check_label(label: &str) -> Result<(), ErrorProblem>;
133

            
134
    /// Convert the bytes of the Object (which was present) into the actual value
135
    ///
136
    /// `input` has been base64-decoded.
137
    fn from_bytes(input: &[u8]) -> Result<Self, ErrorProblem>;
138
}
139

            
140
/// Token indicating that a keyword is structural
141
///
142
/// Returned by [`NetdocParseable::is_structural_keyword`]
143
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
144
#[allow(clippy::exhaustive_structs)]
145
pub struct IsStructural;
146

            
147
//---------- provided blanket impls ----------
148

            
149
impl<T: ItemArgumentParseable> ItemArgumentParseable for Arc<T> {
150
    fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<Self, ArgumentError> {
151
        T::from_args(args).map(Arc::new)
152
    }
153
}
154

            
155
impl<T: ItemArgumentParseable + GloballyInternable + Eq + Hash + 'static> ItemArgumentParseable
156
    for Intern<T>
157
{
158
    fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<Self, ArgumentError> {
159
        T::from_args(args).map(|x| T::into_intern(x))
160
    }
161
}
162

            
163
impl<T: NormalItemArgument + FromStr> ItemArgumentParseable for T {
164
23054
    fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<Self, AE> {
165
23054
        let v = args
166
23054
            .next()
167
23054
            .ok_or(AE::Missing)?
168
23054
            .parse()
169
23054
            .map_err(|_e| AE::Invalid)?;
170
23046
        Ok(v)
171
23054
    }
172
}
173

            
174
impl<T: ItemValueParseable> ItemValueParseable for Arc<T> {
175
72
    fn from_unparsed(item: UnparsedItem<'_>) -> Result<Self, ErrorProblem> {
176
72
        T::from_unparsed(item).map(Arc::new)
177
72
    }
178
}
179

            
180
impl<T: ItemValueParseable + GloballyInternable + Eq + Hash + 'static> ItemValueParseable
181
    for Intern<T>
182
{
183
6
    fn from_unparsed(item: UnparsedItem<'_>) -> Result<Self, ErrorProblem> {
184
9
        T::from_unparsed(item).map(|x| T::into_intern(x))
185
6
    }
186
}
187

            
188
impl<T: NetdocParseable> NetdocParseable for Arc<T> {
189
    fn doctype_for_error() -> &'static str {
190
        T::doctype_for_error()
191
    }
192
    fn is_intro_item_keyword(kw: KeywordRef<'_>) -> bool {
193
        T::is_intro_item_keyword(kw)
194
    }
195
    fn is_structural_keyword(kw: KeywordRef<'_>) -> Option<IsStructural> {
196
        T::is_structural_keyword(kw)
197
    }
198
    fn from_items(input: &mut ItemStream<'_>, stop_at: stop_at!()) -> Result<Self, EP> {
199
        T::from_items(input, stop_at).map(Arc::new)
200
    }
201
}
202
impl<T: NetdocParseableFields> NetdocParseableFields for Arc<T> {
203
    type Accumulator = T::Accumulator;
204
86
    fn is_item_keyword(kw: KeywordRef<'_>) -> bool {
205
86
        T::is_item_keyword(kw)
206
86
    }
207
40
    fn accumulate_item(acc: &mut Self::Accumulator, item: UnparsedItem<'_>) -> Result<(), EP> {
208
40
        T::accumulate_item(acc, item)
209
40
    }
210
10
    fn finish(acc: Self::Accumulator, items: &ItemStream) -> Result<Self, EP> {
211
10
        T::finish(acc, items).map(Arc::new)
212
10
    }
213
}
214

            
215
/// implement [`ItemValueParseable`] for a particular tuple size
216
macro_rules! item_value_parseable_for_tuple {
217
    { $($i:literal)* } => { paste! {
218
        impl< $( [<T$i>]: ItemArgumentParseable, )* >
219
            ItemValueParseable for ( $( [<T$i>], )* )
220
        {
221
5380
            fn from_unparsed(
222
5380
                #[allow(unused_mut)]
223
5380
                mut item: UnparsedItem<'_>,
224
5380
            ) -> Result<Self, ErrorProblem> {
225
5374
                let r = ( $(
226
5086
                    <[<T$i>] as ItemArgumentParseable>::from_args(
227
5086
                        item.args_mut(),
228
5086
                    ).map_err(item.args().error_handler(stringify!($i)))?,
229
388
                )* );
230
5374
                item.check_no_object()?;
231
5374
                Ok(r)
232
5380
            }
233
        }
234
    } }
235
}
236

            
237
item_value_parseable_for_tuple! {}
238
item_value_parseable_for_tuple! { 0 }
239
item_value_parseable_for_tuple! { 0 1 }
240
item_value_parseable_for_tuple! { 0 1 2 }
241
item_value_parseable_for_tuple! { 0 1 2 3 }
242
item_value_parseable_for_tuple! { 0 1 2 3 4 }
243
item_value_parseable_for_tuple! { 0 1 2 3 4 5 }
244
item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 }
245
item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 }
246
item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 8 }
247
item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 8 9 }