Add formatters, utils, schemas and version
Some checks failed
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / test (3.8) (push) Has been cancelled
CI / test (3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / build-package (push) Has been cancelled
Some checks failed
CI / test (3.10) (push) Has been cancelled
CI / test (3.11) (push) Has been cancelled
CI / test (3.12) (push) Has been cancelled
CI / test (3.8) (push) Has been cancelled
CI / test (3.9) (push) Has been cancelled
CI / lint (push) Has been cancelled
CI / typecheck (push) Has been cancelled
CI / build-package (push) Has been cancelled
This commit is contained in:
99
configforge/utils/formatters.py
Normal file
99
configforge/utils/formatters.py
Normal file
@@ -0,0 +1,99 @@
|
||||
"""Output formatting utilities for ConfigForge."""
|
||||
|
||||
import json
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
import yaml
|
||||
|
||||
|
||||
class Formatters:
|
||||
"""Collection of output formatters for different output formats."""
|
||||
|
||||
@staticmethod
|
||||
def json(data: Any, indent: int = 2) -> str:
|
||||
"""Format data as JSON."""
|
||||
return json.dumps(data, indent=indent, ensure_ascii=False)
|
||||
|
||||
@staticmethod
|
||||
def yaml(data: Any) -> str:
|
||||
"""Format data as YAML."""
|
||||
return yaml.safe_dump(data, default_flow_style=False, allow_unicode=True)
|
||||
|
||||
@staticmethod
|
||||
def table(data: List[Dict[str, Any]], columns: Optional[List[str]] = None) -> str:
|
||||
"""Format data as a table."""
|
||||
if not data:
|
||||
return ""
|
||||
|
||||
if columns is None:
|
||||
columns = list(data[0].keys()) if data else []
|
||||
|
||||
if not columns:
|
||||
return ""
|
||||
|
||||
col_widths = {col: len(col) for col in columns}
|
||||
for row in data:
|
||||
for col in columns:
|
||||
if col in row:
|
||||
col_widths[col] = max(col_widths[col], len(str(row.get(col, ""))))
|
||||
|
||||
header = " ".join(col.ljust(col_widths[col]) for col in columns)
|
||||
separator = " ".join("-" * col_widths[col] for col in columns)
|
||||
rows = []
|
||||
|
||||
for row in data:
|
||||
row_str = " ".join(
|
||||
str(row.get(col, "")).ljust(col_widths[col]) for col in columns
|
||||
)
|
||||
rows.append(row_str)
|
||||
|
||||
return "\n".join([header, separator] + rows)
|
||||
|
||||
@staticmethod
|
||||
def validation_error(error: Dict[str, Any], verbose: bool = False) -> str:
|
||||
"""Format a validation error."""
|
||||
path = error.get("path", [])
|
||||
path_str = ".".join(path) if path else "root"
|
||||
|
||||
message = error.get("message", "Unknown error")
|
||||
severity = error.get("severity", "error")
|
||||
|
||||
lines = []
|
||||
lines.append(f"[{severity.upper()}] {path_str}")
|
||||
lines.append(f" Message: {message}")
|
||||
|
||||
if verbose:
|
||||
if "validator" in error:
|
||||
lines.append(f" Validator: {error['validator']}")
|
||||
if "validator_value" in error:
|
||||
lines.append(f" Value: {error['validator_value']}")
|
||||
if "constraint" in error:
|
||||
lines.append(f" Constraint: {error['constraint']}")
|
||||
|
||||
return "\n".join(lines)
|
||||
|
||||
@staticmethod
|
||||
def summary(
|
||||
total: int,
|
||||
passed: int,
|
||||
failed: int,
|
||||
errors: Optional[List[Dict[str, Any]]] = None,
|
||||
) -> str:
|
||||
"""Format a validation summary."""
|
||||
lines = []
|
||||
lines.append("=" * 50)
|
||||
lines.append("Validation Summary")
|
||||
lines.append("=" * 50)
|
||||
lines.append(f"Total: {total}")
|
||||
lines.append(f"Passed: {passed}")
|
||||
lines.append(f"Failed: {failed}")
|
||||
lines.append("-" * 50)
|
||||
|
||||
if failed > 0 and errors:
|
||||
lines.append("\nValidation Errors:")
|
||||
lines.append("-" * 50)
|
||||
for i, error in enumerate(errors, 1):
|
||||
lines.append(f"{i}. {Formatters.validation_error(error)}")
|
||||
lines.append("")
|
||||
|
||||
return "\n".join(lines)
|
||||
Reference in New Issue
Block a user