lint 0.1.3

A versatile linting tool with CLI, MCP, and library interfaces
Documentation
use crate::language_rules::LanguageRule;
use crate::output::{LintMessage, Severity};
use regex::Regex;
use std::path::Path;
use std::sync::LazyLock;

#[derive(Debug, Clone)]
pub struct CSharpStyleRule;

impl LanguageRule for CSharpStyleRule {
    fn name(&self) -> &str {
        "csharp-style"
    }

    fn description(&self) -> &str {
        "Enforces C# naming conventions and style guidelines."
    }

    fn default_severity(&self) -> Severity {
        Severity::Error
    }

    fn check(&self, content: &str, _file_path: &Path) -> Vec<LintMessage> {
        let mut messages = Vec::new();

        for (line_num, line) in content.lines().enumerate() {
            if line.trim().starts_with("class ")
                && line
                    .chars()
                    .next()
                    .map(|c| c.is_lowercase())
                    .unwrap_or(false)
            {
                messages.push(LintMessage::new(
                    line_num + 1,
                    line.find("class").unwrap_or(0),
                    self.default_severity(),
                    "Class name should use PascalCase".to_string(),
                    self.name().to_string(),
                    Some("Rename class: 'class myClass' → 'class MyClass'".to_string()),
                ));
            }
        }

        messages
    }

    fn supports_extension(&self, extension: &str) -> bool {
        extension == "cs"
    }
}

#[derive(Debug, Clone)]
pub struct CSharpConsoleRule;

static CSHARP_CONSOLE_PATTERN: LazyLock<Regex> =
    LazyLock::new(|| Regex::new(r"Console\.(WriteLine?|Write)\s*\(").unwrap());

impl LanguageRule for CSharpConsoleRule {
    fn name(&self) -> &str {
        "no-csharp-console"
    }

    fn description(&self) -> &str {
        "Detects `Console.WriteLine` and similar debug output in C# code."
    }

    fn check(&self, content: &str, _file_path: &Path) -> Vec<LintMessage> {
        let mut messages = Vec::new();
        let pattern = &*CSHARP_CONSOLE_PATTERN;

        for (line_num, line) in content.lines().enumerate() {
            if pattern.is_match(line) && !line.trim_start().starts_with("//") {
                let col = line.find("Console").unwrap_or(0);
                messages.push(LintMessage::new(
                    line_num + 1,
                    col,
                    self.default_severity(),
                    "Console output detected".to_string(),
                    self.name().to_string(),
                    Some("Use ILogger from Microsoft.Extensions.Logging: inject ILogger<T> and call _logger.LogInformation(\"message\")".to_string()),
                ));
            }
        }

        messages
    }

    fn supports_extension(&self, extension: &str) -> bool {
        extension == "cs"
    }
}