154 lines
4.5 KiB
Python
154 lines
4.5 KiB
Python
|
|
"""
|
||
|
|
Built-in formatter plugins for MarkiTect.
|
||
|
|
|
||
|
|
These formatters provide various output formats for MarkiTect data.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import json
|
||
|
|
import yaml
|
||
|
|
from typing import Any
|
||
|
|
|
||
|
|
from ..base import FormatterPlugin, PluginMetadata, PluginType
|
||
|
|
from ..decorators import register_plugin
|
||
|
|
|
||
|
|
|
||
|
|
@register_plugin("json_formatter")
|
||
|
|
class JsonFormatter(FormatterPlugin):
|
||
|
|
"""JSON output formatter."""
|
||
|
|
|
||
|
|
@property
|
||
|
|
def metadata(self) -> PluginMetadata:
|
||
|
|
return PluginMetadata(
|
||
|
|
name="json_formatter",
|
||
|
|
version="1.0.0",
|
||
|
|
description="Format output as JSON",
|
||
|
|
author="MarkiTect Team",
|
||
|
|
plugin_type=PluginType.FORMATTER
|
||
|
|
)
|
||
|
|
|
||
|
|
def format(self, data: Any, **kwargs) -> str:
|
||
|
|
"""Format data as JSON."""
|
||
|
|
indent = kwargs.get('indent', 2)
|
||
|
|
ensure_ascii = kwargs.get('ensure_ascii', False)
|
||
|
|
|
||
|
|
return json.dumps(data, indent=indent, ensure_ascii=ensure_ascii)
|
||
|
|
|
||
|
|
def get_file_extension(self) -> str:
|
||
|
|
"""Get JSON file extension."""
|
||
|
|
return '.json'
|
||
|
|
|
||
|
|
|
||
|
|
@register_plugin("yaml_formatter")
|
||
|
|
class YamlFormatter(FormatterPlugin):
|
||
|
|
"""YAML output formatter."""
|
||
|
|
|
||
|
|
@property
|
||
|
|
def metadata(self) -> PluginMetadata:
|
||
|
|
return PluginMetadata(
|
||
|
|
name="yaml_formatter",
|
||
|
|
version="1.0.0",
|
||
|
|
description="Format output as YAML",
|
||
|
|
author="MarkiTect Team",
|
||
|
|
plugin_type=PluginType.FORMATTER
|
||
|
|
)
|
||
|
|
|
||
|
|
def format(self, data: Any, **kwargs) -> str:
|
||
|
|
"""Format data as YAML."""
|
||
|
|
default_flow_style = kwargs.get('default_flow_style', False)
|
||
|
|
indent = kwargs.get('indent', 2)
|
||
|
|
|
||
|
|
return yaml.dump(data, default_flow_style=default_flow_style, indent=indent)
|
||
|
|
|
||
|
|
def get_file_extension(self) -> str:
|
||
|
|
"""Get YAML file extension."""
|
||
|
|
return '.yaml'
|
||
|
|
|
||
|
|
|
||
|
|
@register_plugin("table_formatter")
|
||
|
|
class TableFormatter(FormatterPlugin):
|
||
|
|
"""Table output formatter for structured data."""
|
||
|
|
|
||
|
|
@property
|
||
|
|
def metadata(self) -> PluginMetadata:
|
||
|
|
return PluginMetadata(
|
||
|
|
name="table_formatter",
|
||
|
|
version="1.0.0",
|
||
|
|
description="Format output as ASCII table",
|
||
|
|
author="MarkiTect Team",
|
||
|
|
plugin_type=PluginType.FORMATTER
|
||
|
|
)
|
||
|
|
|
||
|
|
def format(self, data: Any, **kwargs) -> str:
|
||
|
|
"""Format data as ASCII table."""
|
||
|
|
if not isinstance(data, (list, tuple)):
|
||
|
|
return str(data)
|
||
|
|
|
||
|
|
if not data:
|
||
|
|
return "No data"
|
||
|
|
|
||
|
|
# Handle list of dictionaries (most common case)
|
||
|
|
if isinstance(data[0], dict):
|
||
|
|
return self._format_dict_table(data, **kwargs)
|
||
|
|
|
||
|
|
# Handle simple list
|
||
|
|
return self._format_simple_table(data, **kwargs)
|
||
|
|
|
||
|
|
def _format_dict_table(self, data: list, **kwargs) -> str:
|
||
|
|
"""Format list of dictionaries as table."""
|
||
|
|
if not data:
|
||
|
|
return "No data"
|
||
|
|
|
||
|
|
# Get all unique keys
|
||
|
|
all_keys = set()
|
||
|
|
for item in data:
|
||
|
|
if isinstance(item, dict):
|
||
|
|
all_keys.update(item.keys())
|
||
|
|
|
||
|
|
headers = sorted(all_keys)
|
||
|
|
|
||
|
|
# Calculate column widths
|
||
|
|
col_widths = {}
|
||
|
|
for header in headers:
|
||
|
|
col_widths[header] = len(str(header))
|
||
|
|
for item in data:
|
||
|
|
if isinstance(item, dict) and header in item:
|
||
|
|
col_widths[header] = max(col_widths[header], len(str(item[header])))
|
||
|
|
|
||
|
|
# Build table
|
||
|
|
lines = []
|
||
|
|
|
||
|
|
# Header
|
||
|
|
header_line = "| " + " | ".join(h.ljust(col_widths[h]) for h in headers) + " |"
|
||
|
|
lines.append(header_line)
|
||
|
|
|
||
|
|
# Separator
|
||
|
|
sep_line = "|-" + "-|-".join("-" * col_widths[h] for h in headers) + "-|"
|
||
|
|
lines.append(sep_line)
|
||
|
|
|
||
|
|
# Data rows
|
||
|
|
for item in data:
|
||
|
|
if isinstance(item, dict):
|
||
|
|
row_line = "| " + " | ".join(
|
||
|
|
str(item.get(h, "")).ljust(col_widths[h]) for h in headers
|
||
|
|
) + " |"
|
||
|
|
lines.append(row_line)
|
||
|
|
|
||
|
|
return "\n".join(lines)
|
||
|
|
|
||
|
|
def _format_simple_table(self, data: list, **kwargs) -> str:
|
||
|
|
"""Format simple list as single-column table."""
|
||
|
|
max_width = max(len(str(item)) for item in data)
|
||
|
|
max_width = max(max_width, len("Value"))
|
||
|
|
|
||
|
|
lines = []
|
||
|
|
lines.append(f"| {'Value'.ljust(max_width)} |")
|
||
|
|
lines.append(f"|-{'-' * max_width}-|")
|
||
|
|
|
||
|
|
for item in data:
|
||
|
|
lines.append(f"| {str(item).ljust(max_width)} |")
|
||
|
|
|
||
|
|
return "\n".join(lines)
|
||
|
|
|
||
|
|
def get_file_extension(self) -> str:
|
||
|
|
"""Get table file extension."""
|
||
|
|
return '.txt'
|