summaryrefslogtreecommitdiff
path: root/crates/tor-netdoc/src/parse2/traits.rs
blob: a6fb690a98918952060d6827547e563f81efaf4c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
//! Core model for netdoc parsing

use super::*;

/// A document or section that can be parsed
///
/// Normally [derived](derive_deftly_template_NetdocParseable).
pub trait NetdocParseable: Sized {
    /// Document type for errors, normally its intro keyword
    fn doctype_for_error() -> &'static str;

    /// Is `Keyword` an intro Item Keyword for this kind of document?
    ///
    /// This is used with 1-keyword lookahead, to allow us to push or pop
    /// the parsing state into or out of a sub-document.
    ///
    /// For signatures sections, this should report *every* recognised keyword.
    fn is_intro_item_keyword(kw: KeywordRef<'_>) -> bool;

    /// Parse the document from a stream of Items
    ///
    /// Should stop before reading any keyword matching `stop_at`.
    /// (Except, right at the start.)
    ///
    /// Should also stop before reading a 2nd intro keyword,
    /// so that successive calls to this function can parse
    /// successive sub-documents of this kind.
    ///
    /// Otherwise, should continue until EOF.
    ///
    /// Must check whether the first item is this document's `is_intro_item_keyword`,
    /// and error if not.
    fn from_items(input: &mut ItemStream<'_>, stop_at: stop_at!()) -> Result<Self, ErrorProblem>;
}

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

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

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

    /// Finish
    ///
    /// Resolves the `Accumulator` into the output type.
    /// Generally, this means throwing an error if expected fields were not present.
    fn finish(acc: Self::Accumulator) -> Result<Self, EP>;
}

/// A network document with (unverified) signatures
///
/// Typically implemented automatically, for `FooSigned` structs, as defined by
/// [`#[derive_deftly(NetdocSigned)]`](derive_deftly_template_NetdocSigned).
pub trait NetdocSigned {
    /// The body, ie not including the signatures
    type Body: Sized;
    /// The signatures (the whole signature section)
    type Signatures: Sized;

    /// Inspect the document (and its signatures)
    ///
    /// # Security hazard
    ///
    /// The signature has not been verified, so the returned data must not be trusted.
    fn inspect_unverified(&self) -> (&Self::Body, &Self::Signatures);

    /// Obtain the actual document (and signatures), without verifying
    ///
    /// # Security hazard
    ///
    /// The signature has not been verified, so the returned data must not be trusted.
    fn unwrap_unverified(self) -> (Self::Body, Self::Signatures);

    /// Construct a new `NetdocSigned` from a body and signatures
    ///
    /// (Called by code generated by `#[derive_deftly(NetdocSigned)]`.)
    fn from_parts(body: Self::Body, signatures: Self::Signatures) -> Self;
}

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

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

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

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

//---------- provided blanket impls ----------

impl<T: NormalItemArgument> ItemArgumentParseable for T {
    fn from_args<'s>(args: &mut ArgumentStream<'s>) -> Result<Self, AE> {
        let v = args
            .next()
            .ok_or(AE::Missing)?
            .parse()
            .map_err(|_e| AE::Missing)?;
        Ok(v)
    }
}

impl<T: ItemValueParseable> ItemValueParseable for Arc<T> {
    fn from_unparsed(item: UnparsedItem<'_>) -> Result<Self, ErrorProblem> {
        T::from_unparsed(item).map(Arc::new)
    }
}

impl<T: NetdocParseable> NetdocParseable for Arc<T> {
    fn doctype_for_error() -> &'static str {
        T::doctype_for_error()
    }
    fn is_intro_item_keyword(kw: KeywordRef<'_>) -> bool {
        T::is_intro_item_keyword(kw)
    }
    fn from_items(input: &mut ItemStream<'_>, stop_at: stop_at!()) -> Result<Self, EP> {
        T::from_items(input, stop_at).map(Arc::new)
    }
}
impl<T: NetdocParseableFields> NetdocParseableFields for Arc<T> {
    type Accumulator = T::Accumulator;
    fn is_item_keyword(kw: KeywordRef<'_>) -> bool {
        T::is_item_keyword(kw)
    }
    fn accumulate_item(acc: &mut Self::Accumulator, item: UnparsedItem<'_>) -> Result<(), EP> {
        T::accumulate_item(acc, item)
    }
    fn finish(acc: Self::Accumulator) -> Result<Self, EP> {
        T::finish(acc).map(Arc::new)
    }
}

/// implement [`ItemValueParseable`] for a particular tuple size
macro_rules! item_value_parseable_for_tuple {
    { $($i:literal)* } => { paste! {
        impl< $( [<T$i>]: ItemArgumentParseable, )* >
            ItemValueParseable for ( $( [<T$i>], )* )
        {
            fn from_unparsed(
                #[allow(unused_mut)]
                mut item: UnparsedItem<'_>,
            ) -> Result<Self, ErrorProblem> {
                let r = ( $(
                    <[<T$i>] as ItemArgumentParseable>::from_args(
                        item.args_mut(),
                    ).map_err(item.args().error_handler(stringify!($i)))?,
                )* );
                item.check_no_object()?;
                Ok(r)
            }
        }
    } }
}

item_value_parseable_for_tuple! {}
item_value_parseable_for_tuple! { 0 }
item_value_parseable_for_tuple! { 0 1 }
item_value_parseable_for_tuple! { 0 1 2 }
item_value_parseable_for_tuple! { 0 1 2 3 }
item_value_parseable_for_tuple! { 0 1 2 3 4 }
item_value_parseable_for_tuple! { 0 1 2 3 4 5 }
item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 }
item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 }
item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 8 }
item_value_parseable_for_tuple! { 0 1 2 3 4 5 6 7 8 9 }