from __future__ import annotations import html from pathlib import Path from typing import Any def _escape(value: object) -> str: return html.escape(str(value), quote=True) def _indicator_rows(audit: dict[str, Any]) -> str: indicators = audit.get("review", {}).get("indicators", []) rows: list[str] = [] if isinstance(indicators, list): for indicator in indicators: if not isinstance(indicator, dict): continue rows.append( "" f"{_escape(indicator.get('line', ''))}" f"{_escape(indicator.get('kind', ''))}" f"{_escape(indicator.get('name', ''))}" f"{_escape(indicator.get('reason', ''))}" "" ) return "\n".join(rows) or 'No review indicators found.' def _option_rows(options: dict[str, object]) -> str: return "\n".join( f"{_escape(key)}{_escape(value)}" for key, value in sorted(options.items()) ) def render_report(*, audit: dict[str, Any], options: dict[str, object], stats: dict[str, object] | None = None) -> str: review = audit.get("review", {}) stats = stats or {} return f""" Patchwork Build Report

Patchwork Build Report

Source Audit

Path: {_escape(audit.get('path'))}

SHA-256: {_escape(audit.get('sha256'))}

Lines: {_escape(audit.get('lines'))}

AST nodes: {_escape(audit.get('ast_nodes'))}

Review indicators: {_escape(review.get('indicator_count', 0))}

Output Stats

Output SHA-256: {_escape(stats.get('output_sha256', 'not generated'))}

Input bytes: {_escape(stats.get('input_bytes', 'not generated'))}

Output bytes: {_escape(stats.get('output_bytes', 'not generated'))}

Expansion ratio: {_escape(stats.get('ratio', 'not generated'))}

Options

{_option_rows(options)}
OptionValue

Review Indicators

{_indicator_rows(audit)}
LineKindNameReason
""" def write_report(path: str | Path, *, audit: dict[str, Any], options: dict[str, object], stats: dict[str, object] | None = None) -> Path: output_path = Path(path) output_path.parent.mkdir(parents=True, exist_ok=True) output_path.write_text(render_report(audit=audit, options=options, stats=stats), encoding="utf-8") return output_path