from abc import ABC, abstractmethod from typing import List, TextIO, Dict, Any from errorfix.patterns import MatchResult class Formatter(ABC): @abstractmethod def format(self, results: List[MatchResult], error_text: str) -> str: pass @abstractmethod def write(self, results: List[MatchResult], error_text: str, output: TextIO): pass class TextFormatter(Formatter): def __init__(self, use_colors: bool = True): self.use_colors = use_colors self._colors = { 'reset': '\033[0m', 'red': '\033[31m', 'green': '\033[32m', 'yellow': '\033[33m', 'blue': '\033[34m', 'magenta': '\033[35m', } def _color(self, text: str, color: str) -> str: if not self.use_colors: return text return f"{self._colors.get(color, '')}{text}{self._colors['reset']}" def format(self, results: List[MatchResult], error_text: str) -> str: if not results: return self._format_no_match(error_text) output = [] for i, result in enumerate(results, 1): output.append(self._format_result(i, result)) return '\n'.join(output) def _format_result(self, index: int, result: MatchResult) -> str: lines = [] lines.append(self._color(f"Fix #{index}: {result.rule.name}", 'magenta')) lines.append(self._color("-" * 50, 'blue')) lines.append(f"Rule ID: {result.rule.id}") lines.append(f"Description: {result.rule.description}") lines.append(f"Severity: {result.rule.severity.value}") if result.rule.language: lines.append(f"Language: {result.rule.language}") if result.rule.tool: lines.append(f"Tool: {result.rule.tool}") lines.append("") lines.append(self._color("Matched:", 'yellow')) lines.append(result.matched_text) lines.append("") lines.append(self._color("Suggested Fix:", 'green')) fix = result.apply_fix() lines.append(fix) if result.groups: lines.append("") lines.append(self._color("Captured Variables:", 'yellow')) for key, value in result.groups.items(): lines.append(f" {key} = {value}") return '\n'.join(lines) def _format_no_match(self, error_text: str) -> str: lines = [] lines.append(self._color("No matching rules found", 'yellow')) lines.append("-" * 50) lines.append("Error text:") lines.append(error_text[:200] + "..." if len(error_text) > 200 else error_text) lines.append("") lines.append("Suggestions:") lines.append(" - Check if the correct rule files are loaded") lines.append(" - Verify the error pattern matches any known rules") lines.append(" - Add a custom rule for this error type") return '\n'.join(lines) def write(self, results: List[MatchResult], error_text: str, output: TextIO): output.write(self.format(results, error_text)) class JSONFormatter(Formatter): def __init__(self, pretty: bool = True): self.pretty = pretty def format(self, results: List[MatchResult], error_text: str) -> str: data = self._to_dict(results, error_text) if self.pretty: import json return json.dumps(data, indent=2) import json return json.dumps(data) def _to_dict(self, results: List[MatchResult], error_text: str) -> Dict[str, Any]: return { 'input_text': error_text, 'match_count': len(results), 'matches': [ { 'rule': { 'id': r.rule.id, 'name': r.rule.name, 'description': r.rule.description, 'severity': r.rule.severity.value if hasattr(r.rule.severity, 'value') else str(r.rule.severity), 'language': r.rule.language, 'tool': r.rule.tool, 'tags': r.rule.tags, }, 'matched_text': r.matched_text, 'captured_variables': r.groups, 'suggested_fix': r.apply_fix(), } for r in results ] } def write(self, results: List[MatchResult], error_text: str, output: TextIO): output.write(self.format(results, error_text)) class StructuredFormatter(Formatter): def format(self, results: List[MatchResult], error_text: str) -> str: output = [] for result in results: output.append({ 'rule_id': result.rule.id, 'rule_name': result.rule.name, 'match': result.matched_text, 'fix': result.apply_fix(), 'variables': result.groups, }) return str(output) def write(self, results: List[MatchResult], error_text: str, output: TextIO): output.write(self.format(results, error_text))