1use thiserror::Error;
2
3#[derive(Debug, Clone, PartialEq)]
4pub struct Position {
5 pub line: usize,
6 pub column: usize,
7 pub offset: usize,
8}
9
10impl Position {
11 pub fn new(line: usize, column: usize, offset: usize) -> Self {
12 Self {
13 line,
14 column,
15 offset,
16 }
17 }
18}
19
20#[derive(Debug, Clone, PartialEq)]
21pub struct Span {
22 pub start: Position,
23 pub end: Position,
24}
25
26impl Span {
27 pub fn new(start: Position, end: Position) -> Self {
28 Self { start, end }
29 }
30
31 pub fn single(pos: Position) -> Self {
32 Self {
33 start: pos.clone(),
34 end: pos,
35 }
36 }
37}
38
39#[derive(Error, Debug, Clone, PartialEq)]
40pub enum LintError {
41 #[error("Lexer error at {position:?}: {message}")]
42 LexerError { position: Position, message: String },
43
44 #[error("Parser error at {span:?}: {message}")]
45 ParserError { span: Span, message: String },
46
47 #[error("Validation error at {span:?}: {message}")]
48 ValidationError { span: Span, message: String },
49
50 #[error("Boolean operator '{operator}' must be capitalized at {span:?}")]
51 InvalidBooleanCase { span: Span, operator: String },
52
53 #[error("Unbalanced parentheses at {span:?}")]
54 UnbalancedParentheses { span: Span },
55
56 #[error(
57 "Invalid wildcard placement at {span:?}: wildcards cannot be at the beginning of a word"
58 )]
59 InvalidWildcardPlacement { span: Span },
60
61 #[error("Invalid proximity operator syntax at {span:?}: {message}")]
62 InvalidProximityOperator { span: Span, message: String },
63
64 #[error("Invalid field operator syntax at {span:?}: {message}")]
65 InvalidFieldOperator { span: Span, message: String },
66
67 #[error("Invalid range syntax at {span:?}: expected '[value TO value]'")]
68 InvalidRangeSyntax { span: Span },
69
70 #[error("Unexpected token '{token}' at {span:?}")]
71 UnexpectedToken { span: Span, token: String },
72
73 #[error("Expected '{expected}' but found '{found}' at {span:?}")]
74 ExpectedToken {
75 span: Span,
76 expected: String,
77 found: String,
78 },
79}
80
81#[derive(Debug, Clone, PartialEq)]
82pub enum LintWarning {
83 PotentialTypo { span: Span, suggestion: String },
84 DeprecatedOperator { span: Span, replacement: String },
85 PerformanceWarning { span: Span, message: String },
86}
87
88pub type LintResult<T> = Result<T, LintError>;
89
90#[derive(Debug, Clone, PartialEq, Default)]
91pub struct LintReport {
92 pub errors: Vec<LintError>,
93 pub warnings: Vec<LintWarning>,
94}
95
96impl LintReport {
97 pub fn new() -> Self {
98 Self {
99 errors: Vec::new(),
100 warnings: Vec::new(),
101 }
102 }
103
104 pub fn add_error(&mut self, error: LintError) {
105 self.errors.push(error);
106 }
107
108 pub fn add_warning(&mut self, warning: LintWarning) {
109 self.warnings.push(warning);
110 }
111
112 pub fn has_errors(&self) -> bool {
113 !self.errors.is_empty()
114 }
115
116 pub fn has_warnings(&self) -> bool {
117 !self.warnings.is_empty()
118 }
119
120 pub fn is_clean(&self) -> bool {
121 self.errors.is_empty() && self.warnings.is_empty()
122 }
123}