"""Code scanning logic.""" from pathlib import Path from typing import Optional from .models import ScanResult, Issue, IssueCategory, SeverityLevel from .options import ScanOptions class CodeScanner: """Scan code files for issues.""" SUPPORTED_EXTENSIONS = {".py", ".js", ".ts", ".jsx", ".tsx"} def __init__(self): self.issues: list[Issue] = [] def scan( self, path: Path, options: Optional[ScanOptions] = None ) -> ScanResult: """Scan a file or directory for issues.""" options = options or ScanOptions() result = ScanResult() files = self._collect_files(path) result.files_scanned = len(files) for file_path in files: issues = self._scan_file(file_path, options) result.issues.extend(issues) return result def _collect_files(self, path: Path) -> list[Path]: """Collect files to scan.""" if path.is_file() and self._is_supported(path): return [path] files = [] for match in path.rglob("*"): if match.is_file() and self._is_supported(match): files.append(match) return files def _is_supported(self, path: Path) -> bool: """Check if file extension is supported.""" return path.suffix in self.SUPPORTED_EXTENSIONS def _scan_file(self, path: Path, options: ScanOptions) -> list[Issue]: """Scan a single file for issues.""" issues = [] content = path.read_text(errors="ignore") lines = content.split("\n") for i, line in enumerate(lines, 1): if "TODO" in line or "FIXME" in line: issues.append( Issue( category=IssueCategory.MAINTAINABILITY, severity=SeverityLevel.LOW, file_path=str(path), line_number=i, message="TODO/FIXME comment found", ) ) return issues