1use std::fmt;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum ParserError {
6 UnexpectedToken {
8 line: u64,
9 column: u64,
10 expected: String,
11 found: String,
12 },
13
14 ExpectedToken {
16 line: u64,
17 column: u64,
18 expected: String,
19 found: String,
20 },
21
22 UnterminatedString { line: u64, column: u64 },
24
25 InvalidNumber {
27 line: u64,
28 column: u64,
29 value: String,
30 },
31
32 InvalidVector { line: u64, column: u64 },
34
35 RecursionLimitExceeded { depth: usize },
37
38 InternalParserDefect { message: String },
45}
46
47impl fmt::Display for ParserError {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 Self::UnexpectedToken {
51 line,
52 column,
53 expected,
54 found,
55 } => write!(
56 f,
57 "error[ALOPEX-P001]: unexpected token at line {line}, column {column}: expected {expected}, found {found}"
58 ),
59 Self::ExpectedToken {
60 line,
61 column,
62 expected,
63 found,
64 } => write!(
65 f,
66 "error[ALOPEX-P002]: expected {expected} but found {found} at line {line}, column {column}"
67 ),
68 Self::UnterminatedString { line, column } => write!(
69 f,
70 "error[ALOPEX-P003]: unterminated string literal starting at line {line}, column {column}"
71 ),
72 Self::InvalidNumber {
73 line,
74 column,
75 value,
76 } => write!(
77 f,
78 "error[ALOPEX-P004]: invalid number literal '{value}' at line {line}, column {column}"
79 ),
80 Self::InvalidVector { line, column } => write!(
81 f,
82 "error[ALOPEX-P005]: invalid vector literal at line {line}, column {column}"
83 ),
84 Self::RecursionLimitExceeded { depth } => write!(
85 f,
86 "error[ALOPEX-P006]: recursion limit exceeded (depth: {depth})"
87 ),
88 Self::InternalParserDefect { message } => write!(
89 f,
90 "error[ALOPEX-P007]: internal parser defect (this is a parser bug, not invalid SQL): {message}"
91 ),
92 }
93 }
94}
95
96impl std::error::Error for ParserError {}
97
98pub type Result<T> = std::result::Result<T, ParserError>;
100
101#[cfg(test)]
102mod tests {
103 use super::*;
104
105 #[test]
106 fn internal_parser_defect_is_labeled_with_its_own_error_code() {
107 let defect = ParserError::InternalParserDefect {
111 message: "field 'children' is not accessible".to_string(),
112 };
113 let rendered = defect.to_string();
114 assert!(rendered.contains("ALOPEX-P007"));
115 assert!(rendered.contains("field 'children' is not accessible"));
116
117 let token = ParserError::UnexpectedToken {
118 line: 1,
119 column: 2,
120 expected: "expr".to_string(),
121 found: "EOF".to_string(),
122 };
123 assert!(token.to_string().contains("ALOPEX-P001"));
124 assert_ne!(rendered, token.to_string());
125 }
126}