aldrin_parser/error/
invalid_syntax.rs

1use super::Error;
2use crate::diag::{Diagnostic, DiagnosticKind, Formatted, Formatter};
3use crate::grammar::Rule;
4use crate::{LineCol, Parsed, Position, Span};
5use std::borrow::Cow;
6use std::collections::BTreeSet;
7
8#[derive(Debug)]
9pub struct InvalidSyntax {
10    schema_name: String,
11    pos: Position,
12    expected: BTreeSet<Expected>,
13}
14
15impl InvalidSyntax {
16    pub(crate) fn new<S>(schema_name: S, err: pest::error::Error<Rule>) -> Self
17    where
18        S: Into<String>,
19    {
20        use pest::error::ErrorVariant;
21
22        let pos = Position::from_pest_error(&err);
23
24        let positives = match err.variant {
25            ErrorVariant::ParsingError { positives, .. } => positives,
26            ErrorVariant::CustomError { .. } => unreachable!(),
27        };
28
29        let mut expected = BTreeSet::new();
30        for rule in positives {
31            Expected::add(rule, &mut expected);
32        }
33
34        Self {
35            schema_name: schema_name.into(),
36            pos,
37            expected,
38        }
39    }
40
41    pub fn position(&self) -> Position {
42        self.pos
43    }
44
45    pub fn expected(&self) -> &BTreeSet<Expected> {
46        &self.expected
47    }
48}
49
50impl Diagnostic for InvalidSyntax {
51    fn kind(&self) -> DiagnosticKind {
52        DiagnosticKind::Error
53    }
54
55    fn schema_name(&self) -> &str {
56        &self.schema_name
57    }
58
59    fn format<'a>(&'a self, parsed: &'a Parsed) -> Formatted<'a> {
60        let mut reason = "expected ".to_owned();
61
62        let mut iter = self.expected.iter().peekable();
63        let mut first = true;
64        let mut eof = false;
65        while let Some(expected) = iter.next() {
66            let expected: Cow<'static, str> = match expected {
67                Expected::Eof => {
68                    eof = true;
69                    continue;
70                }
71                Expected::Ident => "an identifier".into(),
72                Expected::Keyword(kw) => format!("`{kw}`").into(),
73                Expected::LitInt => "an integer literal".into(),
74                Expected::LitPosInt => "a positive integer literal".into(),
75                Expected::LitString => "a string literal".into(),
76                Expected::LitUuid => "a uuid literal".into(),
77                Expected::SchemaName => "a schema name".into(),
78                Expected::Token(tok) => format!("`{tok}`").into(),
79            };
80
81            if first {
82                first = false;
83            } else if iter.peek().is_some() || eof {
84                reason.push_str(", ");
85            } else {
86                reason.push_str(" or ");
87            }
88
89            reason.push_str(&expected);
90        }
91
92        if eof {
93            if first {
94                reason.push_str("end of file");
95            } else {
96                reason.push_str(" or end of file");
97            }
98        }
99
100        let mut fmt = Formatter::new(self, reason);
101
102        if let Some(schema) = parsed.get_schema(&self.schema_name) {
103            let span = Span {
104                from: self.pos,
105                to: Position {
106                    index: self.pos.index + 1,
107                    line_col: LineCol {
108                        line: self.pos.line_col.line,
109                        column: self.pos.line_col.column + 1,
110                    },
111                },
112            };
113
114            fmt.main_block(schema, self.pos, span, "");
115        }
116
117        fmt.format()
118    }
119}
120
121impl From<InvalidSyntax> for Error {
122    fn from(e: InvalidSyntax) -> Self {
123        Self::InvalidSyntax(e)
124    }
125}
126
127#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
128pub enum Expected {
129    Eof,
130    Ident,
131    Keyword(&'static str),
132    LitInt,
133    LitPosInt,
134    LitString,
135    LitUuid,
136    SchemaName,
137    Token(&'static str),
138}
139
140impl Expected {
141    fn add(rule: Rule, set: &mut BTreeSet<Self>) {
142        const CONST_VALUE: &[Expected] = &[
143            Expected::Keyword("i16"),
144            Expected::Keyword("i32"),
145            Expected::Keyword("i64"),
146            Expected::Keyword("i8"),
147            Expected::Keyword("string"),
148            Expected::Keyword("u16"),
149            Expected::Keyword("u32"),
150            Expected::Keyword("u64"),
151            Expected::Keyword("u8"),
152            Expected::Keyword("uuid"),
153        ];
154
155        const DEF: &[Expected] = &[
156            Expected::Keyword("const"),
157            Expected::Keyword("enum"),
158            Expected::Keyword("service"),
159            Expected::Keyword("struct"),
160            Expected::Token("#"),
161        ];
162
163        const TYPE_NAME: &[Expected] = &[
164            Expected::Ident,
165            Expected::Keyword("bool"),
166            Expected::Keyword("bytes"),
167            Expected::Keyword("f32"),
168            Expected::Keyword("f64"),
169            Expected::Keyword("i16"),
170            Expected::Keyword("i32"),
171            Expected::Keyword("i64"),
172            Expected::Keyword("i8"),
173            Expected::Keyword("lifetime"),
174            Expected::Keyword("map"),
175            Expected::Keyword("object_id"),
176            Expected::Keyword("option"),
177            Expected::Keyword("receiver"),
178            Expected::Keyword("result"),
179            Expected::Keyword("sender"),
180            Expected::Keyword("service_id"),
181            Expected::Keyword("set"),
182            Expected::Keyword("string"),
183            Expected::Keyword("u16"),
184            Expected::Keyword("u32"),
185            Expected::Keyword("u64"),
186            Expected::Keyword("u8"),
187            Expected::Keyword("unit"),
188            Expected::Keyword("uuid"),
189            Expected::Keyword("value"),
190            Expected::Keyword("vec"),
191            Expected::SchemaName,
192            Expected::Token("["),
193        ];
194
195        const INLINE: &[Expected] = &[Expected::Keyword("enum"), Expected::Keyword("struct")];
196
197        const KEY_TYPE_NAME: &[Expected] = &[
198            Expected::Keyword("i16"),
199            Expected::Keyword("i32"),
200            Expected::Keyword("i64"),
201            Expected::Keyword("i8"),
202            Expected::Keyword("string"),
203            Expected::Keyword("u16"),
204            Expected::Keyword("u32"),
205            Expected::Keyword("u64"),
206            Expected::Keyword("u8"),
207            Expected::Keyword("uuid"),
208        ];
209
210        const ARRAY_LEN: &[Expected] =
211            &[Expected::Ident, Expected::LitPosInt, Expected::SchemaName];
212
213        #[allow(clippy::use_self)]
214        let add: &[&[Self]] = match rule {
215            Rule::EOI => &[&[Expected::Eof]],
216            Rule::array_len => &[ARRAY_LEN],
217            Rule::const_value => &[CONST_VALUE],
218            Rule::def => &[DEF],
219            Rule::ident => &[&[Expected::Ident]],
220            Rule::key_type_name => &[KEY_TYPE_NAME],
221            Rule::kw_args => &[&[Expected::Keyword("args")]],
222            Rule::kw_enum => &[&[Expected::Keyword("enum")]],
223            Rule::kw_err => &[&[Expected::Keyword("err")]],
224            Rule::kw_fallback => &[&[Expected::Keyword("fallback")]],
225            Rule::kw_import => &[&[Expected::Keyword("import")]],
226            Rule::kw_object_id => &[&[Expected::Keyword("object_id")]],
227            Rule::kw_ok => &[&[Expected::Keyword("ok")]],
228            Rule::kw_service_id => &[&[Expected::Keyword("service_id")]],
229            Rule::kw_struct => &[&[Expected::Keyword("struct")]],
230            Rule::kw_uuid => &[&[Expected::Keyword("uuid")]],
231            Rule::kw_version => &[&[Expected::Keyword("version")]],
232            Rule::lit_int => &[&[Expected::LitInt]],
233            Rule::lit_pos_int => &[&[Expected::LitPosInt]],
234            Rule::lit_string => &[&[Expected::LitString]],
235            Rule::lit_uuid => &[&[Expected::LitUuid]],
236            Rule::schema_name => &[&[Expected::SchemaName]],
237            Rule::service_item => &[&[Expected::Keyword("fn"), Expected::Keyword("event")]],
238            Rule::struct_field => &[&[Expected::Keyword("required"), Expected::Ident]],
239            Rule::tok_ang_close => &[&[Expected::Token(">")]],
240            Rule::tok_ang_open => &[&[Expected::Token("<")]],
241            Rule::tok_arrow => &[&[Expected::Token("->")]],
242            Rule::tok_at => &[&[Expected::Token("@")]],
243            Rule::tok_comma => &[&[Expected::Token(",")]],
244            Rule::tok_cur_close => &[&[Expected::Token("}")]],
245            Rule::tok_cur_open => &[&[Expected::Token("{")]],
246            Rule::tok_eq => &[&[Expected::Token("=")]],
247            Rule::tok_hash => &[&[Expected::Token("#")]],
248            Rule::tok_par_close => &[&[Expected::Token(")")]],
249            Rule::tok_par_open => &[&[Expected::Token("(")]],
250            Rule::tok_scope => &[&[Expected::Token("::")]],
251            Rule::tok_squ_close => &[&[Expected::Token("]")]],
252            Rule::tok_squ_open => &[&[Expected::Token("[")]],
253            Rule::tok_term => &[&[Expected::Token(";")]],
254            Rule::type_name => &[TYPE_NAME],
255            Rule::type_name_or_inline => &[TYPE_NAME, INLINE],
256            _ => return,
257        };
258
259        for &slice in add {
260            set.extend(slice);
261        }
262    }
263}