Skip to main content

lint/rules/
custom.rs

1use crate::output::{LintMessage, Severity};
2use crate::rules::Rule;
3use regex::Regex;
4use serde::Deserialize;
5use std::path::Path;
6
7#[derive(Debug, Clone, Deserialize)]
8pub struct CustomRuleDefinition {
9    pub name: String,
10    pub pattern: String,
11    pub message: String,
12    pub severity: String,
13    pub suggestion: Option<String>,
14    pub extensions: Option<Vec<String>>,
15}
16
17#[derive(Debug, Clone)]
18pub struct CustomRule {
19    definition: CustomRuleDefinition,
20    regex: Regex,
21}
22
23impl CustomRule {
24    pub fn from_definition(def: CustomRuleDefinition) -> Result<Self, regex::Error> {
25        let regex = Regex::new(&def.pattern)?;
26        Ok(Self {
27            definition: def,
28            regex,
29        })
30    }
31}
32
33impl Rule for CustomRule {
34    fn name(&self) -> &str {
35        &self.definition.name
36    }
37
38    fn category(&self) -> &str {
39        "custom"
40    }
41
42    fn check(&self, content: &str, file_path: &Path) -> Vec<LintMessage> {
43        if let Some(ref extensions) = self.definition.extensions {
44            let ext_matches = file_path.extension().is_some_and(|ext| {
45                let ext_str = ext.to_string_lossy();
46                extensions.iter().any(|e| e == ext_str.as_ref())
47            });
48            if !ext_matches {
49                return Vec::new();
50            }
51        }
52
53        let mut messages = Vec::new();
54        let severity = match self.definition.severity.as_str() {
55            "Error" => Severity::Error,
56            "Warning" => Severity::Warning,
57            _ => Severity::Info,
58        };
59
60        for (line_num, line) in content.lines().enumerate() {
61            if self.regex.is_match(line) {
62                messages.push(LintMessage::new(
63                    line_num + 1,
64                    0,
65                    severity,
66                    self.definition.message.clone(),
67                    self.definition.name.clone(),
68                    self.definition.suggestion.clone(),
69                ));
70            }
71        }
72
73        messages
74    }
75}