Skip to main content

bwq_lint/
error.rs

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}