fix: resolve CI linting failures and type errors
Some checks failed
CI / test (push) Has been cancelled
Some checks failed
CI / test (push) Has been cancelled
This commit is contained in:
154
app/errorfix/cli.py
Normal file
154
app/errorfix/cli.py
Normal file
@@ -0,0 +1,154 @@
|
||||
import sys
|
||||
import os
|
||||
from typing import Optional, Tuple, IO
|
||||
|
||||
import click
|
||||
|
||||
from errorfix.rules import RuleLoader
|
||||
from errorfix.patterns import PatternMatcher
|
||||
from errorfix.formatters import TextFormatter, JSONFormatter, StructuredFormatter, Formatter
|
||||
from errorfix.plugins import PluginLoader
|
||||
|
||||
|
||||
def get_rule_loader() -> RuleLoader:
|
||||
return RuleLoader()
|
||||
|
||||
|
||||
def get_pattern_matcher() -> PatternMatcher:
|
||||
return PatternMatcher()
|
||||
|
||||
|
||||
def get_plugin_loader() -> PluginLoader:
|
||||
return PluginLoader()
|
||||
|
||||
|
||||
@click.group()
|
||||
@click.option('--rules-path', '-r', multiple=True, help='Path to rule files or directories')
|
||||
@click.option('--plugin', '-p', multiple=True, help='Plugin modules to load')
|
||||
@click.option('--verbose', '-v', is_flag=True, help='Enable verbose output')
|
||||
@click.pass_context
|
||||
def cli(ctx: click.Context, rules_path: Tuple[str], plugin: Tuple[str], verbose: bool):
|
||||
ctx.ensure_object(dict)
|
||||
ctx.obj['rules_path'] = list(rules_path)
|
||||
ctx.obj['plugins'] = list(plugin)
|
||||
ctx.obj['verbose'] = verbose
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('--input', '-i', 'error_input', type=click.File('r'), default='-', help='Input file or stdin')
|
||||
@click.option('--output-format', '-f', type=click.Choice(['text', 'json', 'structured']), default='text', help='Output format')
|
||||
@click.option('--language', '-l', help='Filter rules by language')
|
||||
@click.option('--tool', '-t', help='Filter rules by tool')
|
||||
@click.option('--limit', type=int, default=None, help='Limit number of matches')
|
||||
@click.option('--no-color', is_flag=True, help='Disable colored output')
|
||||
@click.option('--rules', '-r', multiple=True, help='Additional rule paths')
|
||||
@click.pass_context
|
||||
def fix(
|
||||
ctx: click.Context,
|
||||
error_input: IO[str],
|
||||
output_format: str,
|
||||
language: Optional[str],
|
||||
tool: Optional[str],
|
||||
limit: Optional[int],
|
||||
no_color: bool,
|
||||
rules: Tuple[str]
|
||||
):
|
||||
if error_input == sys.stdin and hasattr(sys.stdin, 'closed') and sys.stdin.closed:
|
||||
error_text = ''
|
||||
else:
|
||||
error_text = ''
|
||||
if error_input and error_input != '-':
|
||||
try:
|
||||
error_text = error_input.read()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
rule_loader = get_rule_loader()
|
||||
matcher = get_pattern_matcher()
|
||||
plugin_loader = get_plugin_loader()
|
||||
|
||||
all_rules_paths = list(ctx.obj.get('rules_path', [])) + list(rules)
|
||||
|
||||
rules_list = []
|
||||
for path in all_rules_paths:
|
||||
try:
|
||||
if os.path.exists(path):
|
||||
rules_list.extend(rule_loader.load_multiple([path]))
|
||||
except Exception as e:
|
||||
if ctx.obj.get('verbose'):
|
||||
click.echo(f"Warning: Failed to load rules from {path}: {e}", err=True)
|
||||
|
||||
for plugin_name in ctx.obj.get('plugins', []):
|
||||
try:
|
||||
plugin_loader.load_plugin_module(plugin_name)
|
||||
plugin_rules = plugin_loader.get_registry().get_all_rules()
|
||||
from errorfix.rules import Rule
|
||||
rules_list.extend([Rule.from_dict(r) for r in plugin_rules])
|
||||
except Exception as e:
|
||||
click.echo(f"Warning: Failed to load plugin {plugin_name}: {e}", err=True)
|
||||
|
||||
if not rules_list:
|
||||
default_rules_path = os.environ.get('ERRORFIX_RULES_PATH', 'rules')
|
||||
if os.path.exists(default_rules_path):
|
||||
try:
|
||||
rules_list = rule_loader.load_directory(default_rules_path)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if language:
|
||||
rules_list = rule_loader.filter_rules(rules_list, language=language)
|
||||
if tool:
|
||||
rules_list = rule_loader.filter_rules(rules_list, tool=tool)
|
||||
|
||||
matches = matcher.match_all(error_text, rules_list, limit=limit)
|
||||
|
||||
if output_format == 'json':
|
||||
formatter: Formatter = JSONFormatter(pretty=not no_color)
|
||||
elif output_format == 'structured':
|
||||
formatter = StructuredFormatter()
|
||||
else:
|
||||
formatter = TextFormatter(use_colors=not no_color)
|
||||
|
||||
output = formatter.format(matches, error_text)
|
||||
click.echo(output)
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('--dry-run', is_flag=True, help='Show what would be fixed without applying')
|
||||
@click.pass_context
|
||||
def interactive(ctx: click.Context, dry_run: bool):
|
||||
click.echo("Interactive mode not yet implemented. Use --dry-run for preview.")
|
||||
|
||||
|
||||
@cli.command()
|
||||
def plugins():
|
||||
plugin_loader = get_plugin_loader()
|
||||
plugin_list = plugin_loader.discover_and_load()
|
||||
for plugin in plugin_list:
|
||||
click.echo(f"{plugin.name} v{plugin.version}: {plugin.description}")
|
||||
|
||||
|
||||
@cli.command()
|
||||
@click.option('--rules', '-r', multiple=True, help='Rule paths to check')
|
||||
@click.pass_context
|
||||
def check(ctx: click.Context, rules: Tuple[str]):
|
||||
rule_loader = get_rule_loader()
|
||||
all_rules_paths = list(ctx.obj.get('rules_path', [])) + list(rules)
|
||||
|
||||
for path in all_rules_paths:
|
||||
try:
|
||||
if os.path.exists(path):
|
||||
rules_list = rule_loader.load_multiple([path])
|
||||
click.echo(f"Loaded {len(rules_list)} rules from {path}")
|
||||
else:
|
||||
click.echo(f"Path not found: {path}")
|
||||
except Exception as e:
|
||||
click.echo(f"Error loading {path}: {e}", err=True)
|
||||
|
||||
|
||||
def main():
|
||||
cli()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Reference in New Issue
Block a user