rigsql_rules/
violation.rs1use rigsql_core::Span;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum Severity {
6 Error,
7 Warning,
8}
9
10#[derive(Debug, Clone)]
12pub struct SourceEdit {
13 pub span: Span,
15 pub new_text: String,
17}
18
19impl SourceEdit {
20 pub fn replace(span: Span, new_text: impl Into<String>) -> Self {
22 Self {
23 span,
24 new_text: new_text.into(),
25 }
26 }
27
28 pub fn insert(offset: u32, text: impl Into<String>) -> Self {
30 Self {
31 span: Span::new(offset, offset),
32 new_text: text.into(),
33 }
34 }
35
36 pub fn delete(span: Span) -> Self {
38 Self {
39 span,
40 new_text: String::new(),
41 }
42 }
43}
44
45#[derive(Debug, Clone)]
47pub struct LintViolation {
48 pub rule_code: &'static str,
50 pub message: String,
52 pub span: Span,
54 pub severity: Severity,
56 pub fixes: Vec<SourceEdit>,
58}
59
60impl LintViolation {
61 pub fn new(rule_code: &'static str, message: impl Into<String>, span: Span) -> Self {
62 Self {
63 rule_code,
64 message: message.into(),
65 span,
66 severity: Severity::Warning,
67 fixes: Vec::new(),
68 }
69 }
70
71 pub fn with_fix(
73 rule_code: &'static str,
74 message: impl Into<String>,
75 span: Span,
76 fixes: Vec<SourceEdit>,
77 ) -> Self {
78 Self {
79 rule_code,
80 message: message.into(),
81 span,
82 severity: Severity::Warning,
83 fixes,
84 }
85 }
86
87 pub fn line_col(&self, source: &str) -> (usize, usize) {
89 let offset = (self.span.start as usize).min(source.len());
90 let offset = if source.is_char_boundary(offset) {
92 offset
93 } else {
94 (0..offset)
96 .rev()
97 .find(|&i| source.is_char_boundary(i))
98 .unwrap_or(0)
99 };
100 let before = &source[..offset];
101 let line = before.chars().filter(|&c| c == '\n').count() + 1;
102 let col = before.rfind('\n').map_or(offset, |pos| offset - pos - 1) + 1;
103 (line, col)
104 }
105}