Initial upload: ErrorFix CLI with rule engine and pattern matching
Some checks failed
CI / test (push) Has been cancelled
Some checks failed
CI / test (push) Has been cancelled
This commit is contained in:
139
errorfix/formatters/__init__.py
Normal file
139
errorfix/formatters/__init__.py
Normal file
@@ -0,0 +1,139 @@
|
||||
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))
|
||||
Reference in New Issue
Block a user