pre_rules_guard.py

Code Hygiene Score: 98

Keine Issues gefunden.

Dependencies 6

Funktionen 3

Code

#!/usr/bin/env python3
"""
Pre-Hook Guard Regeln (BLOCK) - SRP + OOP Basics.

P1.x Regeln: Single Responsibility, KISS, Naming
P4.x Regeln: OOP Basics, Encapsulation
"""

import re
from pathlib import Path
from typing import Optional
from .rule_base import GLOBAL_ALLOWLIST, is_in_allowlist, block


# =============================================================================
# PRÜFUNG 1: SRP + KISS
# =============================================================================

def p1_1_responsibility_header(file_path: str, content: str) -> Optional[dict]:
    """P1.1: @responsibility Header erforderlich."""
    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):
        return None

    header_pattern = r"//\s*@responsibility:\s*(.+)"
    match = re.search(header_pattern, content)

    if not match:
        return block("P1.1", "Missing @responsibility header. Add: // @responsibility: <single-responsibility>")

    responsibility_text = match.group(1).strip()

    # Prüfe auf Multi-Responsibility-Wörter
    multi_words = r"\b(und|sowie|außerdem|also|zusätzlich|and|also|additionally)\b"
    if re.search(multi_words, responsibility_text, re.IGNORECASE):
        return block("P1.1", f"@responsibility contains multi-responsibility indicator: '{responsibility_text}'")

    return None


def p1_2_garbage_names(file_path: str, content: str) -> Optional[dict]:
    """P1.2: Müllhalden-Namen blockieren."""
    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):
        return None

    filename = Path(file_path).stem.lower()
    forbidden = ["helper", "utils", "common", "misc", "base"]

    for term in forbidden:
        if term in filename:
            return block("P1.2", f"Forbidden name pattern: '{term}' indicates unclear responsibility")

    return None


# =============================================================================
# PRÜFUNG 4: OOP
# =============================================================================

def p4_1_no_public_properties_in_domain(file_path: str, content: str) -> Optional[dict]:
    """P4.1: Keine public Properties in Domain."""
    if "/Domain/" not in file_path:
        return None
    if is_in_allowlist(file_path, GLOBAL_ALLOWLIST):
        return None

    # public readonly ist erlaubt
    public_props = re.findall(
        r"public\s+(?!readonly|function|const|static\s+function).*\$\w+",
        content
    )

    if public_props:
        return block("P4.1", "Public property in Domain. Use getter methods or public readonly.")

    return None


# =============================================================================
# RULE COLLECTION
# =============================================================================

RULES = [
    p1_1_responsibility_header,
    p1_2_garbage_names,
    p4_1_no_public_properties_in_domain,
]
← Übersicht