1use crate::parser::Rule; use thiserror::Error;
3
4#[derive(Error, Debug)]
5pub enum CelParserError {
6 #[error("Pest parsing error: {0}")]
7 PestError(#[from] Box<pest::error::Error<Rule>>), #[error("Invalid integer literal: '{0}' ({1})")]
10 InvalidIntegerLiteral(String, std::num::ParseIntError),
11
12 #[error("Invalid unsigned integer literal: '{0}' ({1})")]
13 InvalidUintLiteral(String, std::num::ParseIntError),
14
15 #[error("Invalid float literal: '{0}' ({1})")]
16 InvalidFloatLiteral(String, std::num::ParseFloatError),
17
18 #[error("Invalid escape sequence in string/bytes literal: '\\{0}'")]
19 InvalidEscapeSequence(String),
20
21 #[error("Incomplete escape sequence: '{0}'")]
22 IncompleteEscapeSequence(String),
23
24 #[error(
25 "Invalid Unicode escape sequence (must be U+0000 to U+10FFFF, excluding surrogates): '{0}'"
26 )]
27 InvalidUnicodeEscape(String),
28
29 #[error("Invalid byte sequence in string literal (not valid UTF-8)")]
30 InvalidUtf8String(#[from] std::string::FromUtf8Error),
31
32 #[error("Internal parser error: {0}")]
33 InternalError(String), }
35
36impl From<pest::error::Error<Rule>> for CelParserError {
38 fn from(err: pest::error::Error<Rule>) -> Self {
39 CelParserError::PestError(Box::new(err))
40 }
41}
42
43impl CelParserError {
44 pub fn location(&self) -> Option<(usize, usize)> {
63 match self {
64 CelParserError::PestError(e) => match e.line_col {
65 pest::error::LineColLocation::Pos(pos) => Some(pos),
66 pest::error::LineColLocation::Span(start, _) => Some(start),
67 },
68 _ => None, }
70 }
71}
72
73#[cfg(test)]
74mod tests {
75 use crate::parser::parse_cel_program;
77
78 #[test]
79 fn test_error_location_invalid_operator() {
80 let invalid_expr = "a && b & c";
85 let result = parse_cel_program(invalid_expr);
86
87 assert!(result.is_err());
89
90 let err = result.unwrap_err();
92
93 let location = err.location();
95 assert!(
96 location.is_some(),
97 "Expected location information for a parse error"
98 );
99
100 let (line, col) = location.unwrap();
101 assert_eq!(line, 1, "Error should be on line 1");
102 assert_eq!(col, 8, "Error should be at column 8 (the '&')");
104 }
105
106 #[test]
107 fn test_error_location_on_incomplete_expr() {
108 let invalid_expr = "1 + ";
113 let result = parse_cel_program(invalid_expr);
114 assert!(result.is_err());
115 let err = result.unwrap_err();
116 let (line, col) = err.location().expect("Should have location info");
117
118 assert_eq!(line, 1);
119 assert_eq!(
122 col, 5,
123 "Error should be at the end of the expression (col 5)"
124 );
125 }
126
127 #[test]
128 fn test_error_location_multiline() {
129 let invalid_expr = "a ||\n b ? c \n : !";
133 let result = parse_cel_program(invalid_expr);
134 assert!(result.is_err());
135 let err = result.unwrap_err();
136 let (line, col) = err.location().expect("Should have location info");
137
138 assert_eq!(line, 3, "Error should be on line 3");
139 assert_eq!(col, 5, "Error should be at EOI on line 3 (col 5)");
142 }
143}