rested/parser/
error.rs

1use crate::lexer;
2use crate::lexer::locations::Position;
3use crate::lexer::{locations::GetSpan, Token, TokenKind};
4
5use crate::error_meta::ContextualError;
6
7use super::{Parser, Result, TokenCheck};
8
9impl<'source> std::fmt::Display for lexer::Token<'source> {
10    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
11        use TokenKind::*;
12        match self.kind {
13            Url | Linecomment | IllegalToken => {
14                write!(f, "{}<{}>", self.kind, self.text)
15            }
16            kind => write!(f, "{kind}"),
17        }
18    }
19}
20
21#[derive(Debug, Clone, PartialEq, serde::Serialize)]
22pub enum ParseError<'source> {
23    ExpectedToken {
24        found: lexer::Token<'source>,
25        expected: TokenKind,
26    },
27    ExpectedEitherOfTokens {
28        found: lexer::Token<'source>,
29        expected: Box<[TokenKind]>,
30    },
31}
32
33impl<'source> std::error::Error for ParseError<'source> {}
34
35impl<'source> std::fmt::Display for ParseError<'source> {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        let formatted_error = match self {
38            ParseError::ExpectedToken {
39                expected, found, ..
40            } => {
41                format!("expected '{}' but got {}", expected, found)
42            }
43            ParseError::ExpectedEitherOfTokens {
44                found, expected, ..
45            } => {
46                let expected = expected
47                    .iter()
48                    .map(|kind| format!("'{}'", kind))
49                    .collect::<Vec<String>>()
50                    .join(",");
51                format!("expected either one of {} but got {}", expected, found)
52            }
53        };
54
55        f.write_str(&formatted_error)
56    }
57}
58
59#[derive(Debug)]
60pub struct ParserErrors<'source> {
61    pub errors: Box<[ContextualError<ParseError<'source>>]>,
62}
63
64impl<'source> ParserErrors<'source> {
65    pub fn new(errors: Vec<ContextualError<ParseError<'source>>>) -> Self {
66        Self {
67            errors: errors.into(),
68        }
69    }
70}
71
72impl<'source> std::error::Error for ParserErrors<'source> {}
73
74impl<'source> std::fmt::Display for ParserErrors<'source> {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        for err in self.errors.iter() {
77            write!(f, "{err}")?
78        }
79        Ok(())
80    }
81}
82
83#[derive(Debug)]
84pub struct Expectations<'i> {
85    source_code: &'i str,
86    pub start: Position,
87}
88
89impl<'i> Expectations<'i> {
90    pub fn new(parser: &Parser<'i>) -> Self {
91        Self {
92            source_code: parser.lexer.input(),
93            start: parser.curr_token().start,
94        }
95    }
96
97    pub fn expect_peek<'p>(
98        &self,
99        parser: &'p mut Parser<'i>,
100        expected_kind: TokenKind,
101    ) -> Result<'i, &'p Token<'i>> {
102        if parser.peek_token().is(expected_kind) {
103            return Ok(parser.next_token());
104        }
105
106        let error = self.expected_token(parser.next_token(), expected_kind);
107
108        Err(error.into())
109    }
110
111    /// Like `expect_peek` but it doesn't advance the parser.
112    pub fn expect_peek_ahead<'p>(
113        &self,
114        parser: &'p mut Parser<'i>,
115        expected_kind: TokenKind,
116    ) -> Result<'i, &'p Token<'i>> {
117        let peeked = parser.peek_token();
118        if peeked.is(expected_kind) {
119            return Ok(peeked);
120        }
121
122        let error = self.expected_token(peeked, expected_kind);
123
124        Err(error.into())
125    }
126
127    pub fn expect_peek_one_of(
128        &self,
129        parser: &mut Parser<'i>,
130        expected_kinds: &[TokenKind],
131    ) -> Result<'i, ()> {
132        if parser.peek_token().is_one_of(expected_kinds) {
133            return Ok(());
134        }
135
136        let error = self.expected_one_of_tokens(parser.next_token(), expected_kinds);
137
138        Err(error.into())
139    }
140
141    pub fn expected_token(
142        &self,
143        token: &Token<'i>,
144        expected: TokenKind,
145    ) -> ContextualError<ParseError<'i>> {
146        ContextualError::new(
147            ParseError::ExpectedToken {
148                found: token.clone(),
149                expected,
150            },
151            self.start.to_end_of(token.span()),
152            self.source_code,
153        )
154    }
155
156    pub fn expected_one_of_tokens(
157        &self,
158        token: &Token<'i>,
159        expected_kinds: &[TokenKind],
160    ) -> ContextualError<ParseError<'i>> {
161        let mut expected_dedpuded: Vec<TokenKind> = vec![];
162
163        for kind in expected_kinds {
164            if !expected_dedpuded.contains(kind) {
165                expected_dedpuded.push(*kind)
166            }
167        }
168
169        ContextualError::new(
170            ParseError::ExpectedEitherOfTokens {
171                found: token.clone(),
172                expected: expected_dedpuded.into(),
173            },
174            self.start.to_end_of(token.span()),
175            self.source_code,
176        )
177    }
178}
179
180pub struct ErrorsCollector<'source> {
181    pub list: Vec<ContextualError<ParseError<'source>>>,
182}
183
184impl<'source> crate::parser::ast_visit::Visitor<'source> for ErrorsCollector<'source> {
185    fn visit_error(&mut self, err: &ContextualError<ParseError<'source>>) {
186        self.list.push(err.clone());
187    }
188}
189
190#[cfg(test)]
191mod tests {
192    use crate::parser::Parser;
193
194    use insta::assert_ron_snapshot;
195
196    macro_rules! assert_ast {
197        ($input:literal) => {
198            let mut parser = Parser::new($input);
199            let ast = parser.parse();
200
201            insta::with_settings!({
202                 description => $input
203            }, {
204                assert_ron_snapshot!(ast)
205            });
206
207            assert!(!ast.errors().is_empty())
208        };
209    }
210
211    #[test]
212    fn expected_url_after_method() {
213        assert_ast!("get");
214        assert_ast!("post");
215    }
216
217    #[test]
218    fn expected_name_after_header_keyword() {
219        assert_ast!("post http://localhost {header}");
220    }
221
222    #[test]
223    fn expecting_identifier_or_string_lit_after_header_name() {
224        assert_ast!(r#"get http://localhost { header "name" }"#);
225    }
226
227    #[test]
228    fn expecting_request_or_other_attribute_after_attributes() {
229        assert_ast!(
230            r#"
231            @skip
232            @dbg
233            let k = "v"
234            get http://localhost { header "name" k }"#
235        );
236    }
237
238    #[test]
239    fn expecting_commas_between_certain_json_items() {
240        assert_ast!(
241            r#"let o = {
242                 yo: "joe"
243                 hello: "world"
244               }"#
245        );
246        assert_ast!(r#" let o = ["joe" "world"] "#);
247    }
248
249    #[test]
250    fn expecting_partial_block_with_error() {
251        assert_ast!(r#"get /hello { header "test" "value" header }"#);
252    }
253
254    #[test]
255    fn expecting_partial_block_with_missing_body_expr() {
256        assert_ast!(
257            r#"
258get /sdf {
259   header "" s
260   body  }
261"#
262        );
263    }
264
265    #[test]
266    fn expecting_partial_block_with_errors() {
267        assert_ast!(
268            r#"
269get /adsf {
270  header
271  body a
272}
273"#
274        );
275    }
276
277    #[test]
278    fn expecting_partial_object_literal_with_errors() {
279        assert_ast!(
280            r#"
281let b = {
282  "key": value, 
283  "key2": 
284}
285"#
286        );
287    }
288
289    #[test]
290    fn expecting_expression_error_for_parameter_of_unfinished_string() {
291        assert_ast!(
292            r#"
293let a = env(")
294"#
295        );
296
297        assert_ast!(
298            r#"
299let b = [env(")]
300"#
301        );
302
303        assert_ast!(
304            r#"
305let b = {
306    key: env(")
307}
308"#
309        );
310    }
311
312    #[test]
313    fn expected_comma_before_more_parameters() {
314        assert_ast!(r#"env("base" "url")"#);
315        assert_ast!(r#"env("", false 12)"#);
316    }
317
318    #[test]
319    fn expected_closing_curly_after_expression_part_in_template_string() {
320        assert_ast!(r#"`wowee ${"hello"} error ahead ${env("base")`"#);
321        assert_ast!(r#"`wowee ${"hello"} error ahead ${variable_name`"#);
322        assert_ast!(r#"`error ahead ${variable_name `"#);
323    }
324}