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 message_key: String,
54 pub message_params: Vec<(String, String)>,
56 pub span: Span,
58 pub severity: Severity,
60 pub fixes: Vec<SourceEdit>,
62}
63
64impl LintViolation {
65 pub fn new(rule_code: &'static str, message: impl Into<String>, span: Span) -> Self {
66 let message = message.into();
67 Self {
68 rule_code,
69 message_key: String::new(),
70 message_params: Vec::new(),
71 message,
72 span,
73 severity: Severity::Warning,
74 fixes: Vec::new(),
75 }
76 }
77
78 pub fn with_msg_key(
80 rule_code: &'static str,
81 message: impl Into<String>,
82 span: Span,
83 message_key: impl Into<String>,
84 message_params: Vec<(String, String)>,
85 ) -> Self {
86 Self {
87 rule_code,
88 message: message.into(),
89 message_key: message_key.into(),
90 message_params,
91 span,
92 severity: Severity::Warning,
93 fixes: Vec::new(),
94 }
95 }
96
97 pub fn with_fix(
99 rule_code: &'static str,
100 message: impl Into<String>,
101 span: Span,
102 fixes: Vec<SourceEdit>,
103 ) -> Self {
104 let message = message.into();
105 Self {
106 rule_code,
107 message_key: String::new(),
108 message_params: Vec::new(),
109 message,
110 span,
111 severity: Severity::Warning,
112 fixes,
113 }
114 }
115
116 pub fn with_fix_and_msg_key(
118 rule_code: &'static str,
119 message: impl Into<String>,
120 span: Span,
121 fixes: Vec<SourceEdit>,
122 message_key: impl Into<String>,
123 message_params: Vec<(String, String)>,
124 ) -> Self {
125 Self {
126 rule_code,
127 message: message.into(),
128 message_key: message_key.into(),
129 message_params,
130 span,
131 severity: Severity::Warning,
132 fixes,
133 }
134 }
135
136 pub fn line_col(&self, source: &str) -> (usize, usize) {
138 let offset = (self.span.start as usize).min(source.len());
139 let offset = if source.is_char_boundary(offset) {
141 offset
142 } else {
143 (0..offset)
145 .rev()
146 .find(|&i| source.is_char_boundary(i))
147 .unwrap_or(0)
148 };
149 let before = &source[..offset];
150 let line = before.chars().filter(|&c| c == '\n').count() + 1;
151 let col = before.rfind('\n').map_or(offset, |pos| offset - pos - 1) + 1;
152 (line, col)
153 }
154}