""" 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'