yamllint_rs/rules/
base.rs1use crate::{LintIssue, Severity};
2use regex::Regex;
3use std::collections::HashMap;
4
5#[derive(Debug, Clone)]
6pub struct BaseRule<T> {
7 pub config: T,
8 pub severity_override: Option<Severity>,
9}
10
11impl<T> BaseRule<T> {
12 pub fn new(config: T) -> Self {
13 Self {
14 config,
15 severity_override: None,
16 }
17 }
18
19 pub fn with_config(config: T) -> Self {
20 Self {
21 config,
22 severity_override: None,
23 }
24 }
25
26 pub fn config(&self) -> &T {
27 &self.config
28 }
29
30 pub fn set_config(&mut self, config: T) {
31 self.config = config;
32 }
33
34 pub fn get_severity(&self, default_severity: Severity) -> Severity {
35 self.severity_override.unwrap_or(default_severity)
36 }
37
38 pub fn set_severity(&mut self, severity: Severity) {
39 self.severity_override = Some(severity);
40 }
41
42 pub fn has_severity_override(&self) -> bool {
43 self.severity_override.is_some()
44 }
45}
46
47#[derive(Debug, Clone)]
48pub struct BaseRuleWithRegex<T> {
49 pub config: T,
50 pub severity_override: Option<Severity>,
51 pub compiled_patterns: HashMap<String, Regex>,
52}
53
54impl<T> BaseRuleWithRegex<T> {
55 pub fn new(config: T) -> Self {
56 Self {
57 config,
58 severity_override: None,
59 compiled_patterns: HashMap::new(),
60 }
61 }
62
63 pub fn with_config(config: T) -> Self {
64 Self {
65 config,
66 severity_override: None,
67 compiled_patterns: HashMap::new(),
68 }
69 }
70
71 pub fn config(&self) -> &T {
72 &self.config
73 }
74
75 pub fn set_config(&mut self, config: T) {
76 self.config = config;
77 }
78
79 pub fn get_severity(&self, default_severity: Severity) -> Severity {
80 self.severity_override.unwrap_or(default_severity)
81 }
82
83 pub fn set_severity(&mut self, severity: Severity) {
84 self.severity_override = Some(severity);
85 }
86
87 pub fn has_severity_override(&self) -> bool {
88 self.severity_override.is_some()
89 }
90
91 pub fn get_or_compile_pattern(&mut self, pattern: &str) -> Result<&Regex, regex::Error> {
92 if !self.compiled_patterns.contains_key(pattern) {
93 let regex = Regex::new(pattern)?;
94 self.compiled_patterns.insert(pattern.to_string(), regex);
95 }
96 Ok(self.compiled_patterns.get(pattern).unwrap())
97 }
98
99 pub fn get_cached_pattern(&self, pattern: &str) -> &Regex {
100 self.compiled_patterns
101 .get(pattern)
102 .expect("Pattern should be cached")
103 }
104}
105
106pub trait LintIssueBuilder {
107 fn create_issue(line: usize, column: usize, message: String, severity: Severity) -> LintIssue {
108 LintIssue {
109 line,
110 column,
111 message,
112 severity,
113 }
114 }
115
116 fn create_line_issue(
117 line: usize,
118 column: usize,
119 message: String,
120 severity: Severity,
121 ) -> LintIssue {
122 Self::create_issue(line, column, message, severity)
123 }
124}
125
126impl<T> LintIssueBuilder for T {}
127
128pub mod utils {
129 pub fn is_empty_or_comment(line: &str) -> bool {
130 let trimmed = line.trim();
131 trimmed.is_empty() || trimmed.starts_with('#')
132 }
133
134 pub fn calculate_indentation(line: &str) -> usize {
135 line.len() - line.trim_start().len()
136 }
137
138 pub fn has_trailing_whitespace(line: &str) -> bool {
139 line.ends_with(' ') || line.ends_with('\t')
140 }
141
142 pub fn count_trailing_whitespace(line: &str) -> usize {
143 line.len() - line.trim_end().len()
144 }
145
146 pub fn join_lines_preserving_newlines(
147 lines: Vec<String>,
148 original_ends_with_newline: bool,
149 ) -> String {
150 if original_ends_with_newline {
151 lines.join("\n") + "\n"
152 } else {
153 lines.join("\n")
154 }
155 }
156}