summaryrefslogtreecommitdiff
path: root/crates/tor-netdoc/src/parse2.rs
blob: 84964fa3919e0d592181b968d581fc27321712c3 (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
//! New netdoc parsing arrangements, with `derive`
//!
//! # Parsing principles
//!
//! A parseable network document is a type implementing [`NetdocParseable`].
//! usually via the
//! [`NetdocParseable` derive=deftly macro`](crate::derive_deftly_template_NetdocParseable).
//!
//! A document type is responsible for recognising its own heading item.
//! Its parser will also be told other of structural items that it should not consume.
//! The structural lines can then be used to pass control to the appropriate parser.
//!
//! # Ordering
//!
//! We don't parse things into a sorted order.
//! Sorting will be done on output.
// TODO we don't implement deriving output yet.
//!
//! # Types, and signature handling
//!
//! Most top-level network documents are signed somehow.
//! In this case there are three types:
//!
//!   * **`FooSigned`**: a signed `Foo`, with its signatures, not yet verified.
//!     Implements [`NetdocSigned`],
//!     typically by invoking the
//!     [`NetdocSigned` derive macro](crate::derive_deftly_template_NetdocSigned)
//!     on `Foo`.
//!
//!     Type-specific methods are provided for verification,
//!     to obtain a `Foo`.
//!
//!   * **`Foo`**: the body data for the document.
//!     This doesn't contain any signatures.
//!     Having one of these to play with means signatures have already been validated.
//!     Implement `NetdocParseable`, via
//!     [derive](crate::derive_deftly_template_NetdocParseable).
//!
//!   * **`FooSignatures`**: the signatures for a `Foo`.
//!     Implement `NetdocParseable`, via
//!     [derive](crate::derive_deftly_template_NetdocParseable),
//!     with `#[deftly(netdoc(signatures))]`.
//!
//! # Naming conventions
//!
//!   * `DocumentName`: important types,
//!     including network documents or sub-documents,
//!     eg `NetworkStatsuMd` and `RouterVote`,
//!     and types that are generally useful.
//!   * `NddDoucmnetSection`: sections and sub-documents
//!     that the user won't normally need to name.
//!   * `NdiItemValue`: parsed value for a network document Item.
//!     eg `NdiVoteStatus` representing the whole of the RHS of a `vote-status` Item.
//!     Often not needed since `ItemValueParseable` is implemented for suitable tuples.
//!   * `NdaArgumentValue`: parsed value for a single argument;
//!     eg `NdaVoteStatus` representing the `vote` or `status` argument.
//!
//! # Relationship to tor_netdoc::parse
//!
//! This is a completely new parsing approach, based on different principles.
//! The key principle is the recognition of "structural keywords",
//! recursively within a parsing stack, via the p`NetdocParseable`] trait.
//!
//! This allows the parser to be derived.  We have type-driven parsing
//! of whole Documents, Items, and their Arguments and Objects,
//! including of their multiplicity.
//!
//! The different keyword handling means we can't use most of the existing lexer,
//! and need new item parsing API:
//!
//!  * [`NetdocParseable`] trait.
//!  * [`KeywordRef`] type.
//!  * [`ItemStream`], [`UnparsedItem`], [`ArgumentStream`], [`UnparsedObject`].
//!
//! The different error handling means we have our own error types.
//! (The crate's existing parse errors have information that we don't track,
//! and is also a portmanteau error for parsing, writing, and other functions.)
//!
//! Document signing is handled in a more abstract way.
//!
//! Some old netdoc constructs are not supported.
//! For example, the obsolete `opt` prefix on safe-to-ignore Items.
//! The parser may make different decisions about netdocs with anomalous item ordering.

#[doc(hidden)]
#[macro_use]
pub mod internal_prelude;

#[macro_use]
mod structural;

#[macro_use]
mod derive;

mod error;
mod impls;
pub mod keyword;
mod lex;
mod lines;
pub mod multiplicity;
mod signatures;
mod traits;

pub mod poc;

#[cfg(test)]
mod test;

use internal_prelude::*;

pub use error::{ErrorProblem, ParseError, VerifyFailed};
pub use impls::times::NdaSystemTimeDeprecatedSyntax;
pub use keyword::KeywordRef;
pub use lex::{ArgumentStream, ItemStream, NoFurtherArguments, UnparsedItem, UnparsedObject};
pub use lines::{Lines, Peeked, StrExt};
pub use signatures::{
    SignatureHashInputs, SignatureItemParseable, check_validity_time, sig_hash_methods,
};
pub use structural::{StopAt, StopPredicate};
pub use traits::{ItemArgumentParseable, ItemObjectParseable, ItemValueParseable, NetdocParseable};

#[doc(hidden)]
pub use derive::netdoc_parseable_derive_debug;

//---------- parser ----------

/// Common code for `parse_netdoc` and `parse_netdoc_multiple`
///
/// Creates the `ItemStream`, calls `parse_completely`, and handles errors.
fn parse_internal<T, D: NetdocParseable>(
    input: &str,
    file: &str,
    parse_completely: impl FnOnce(&mut ItemStream) -> Result<T, ErrorProblem>,
) -> Result<T, ParseError> {
    let mut items = ItemStream::new(input)?;
    parse_completely(&mut items).map_err(|problem| ParseError {
        problem,
        doctype: D::doctype_for_error(),
        file: file.to_owned(),
        lno: items.lno_for_error(),
    })
}

/// Parse a network document - **toplevel entrypoint**
pub fn parse_netdoc<D: NetdocParseable>(input: &str, file: &str) -> Result<D, ParseError> {
    parse_internal::<_, D>(input, file, |items| {
        let doc = D::from_items(items, StopAt(false))?;
        if let Some(_kw) = items.peek_keyword()? {
            return Err(EP::MultipleDocuments);
        }
        Ok(doc)
    })
}

/// Parse a network document - **toplevel entrypoint**
pub fn parse_netdoc_multiple<D: NetdocParseable>(
    input: &str,
    file: &str,
) -> Result<Vec<D>, ParseError> {
    parse_internal::<_, D>(input, file, |items| {
        let mut docs = vec![];
        while items.peek_keyword()?.is_some() {
            let doc = D::from_items(items, StopAt(false))?;
            docs.push(doc);
        }
        Ok(docs)
    })
}