"""Utilities for rendering execution plan information in CLI output.""" from __future__ import annotations from typing import Any def fallback_plan_summary(execution_plan: Any) -> str: """Build a short plan summary from summary and summary_by_reason. This is used when execution_plan.human_summary is missing or empty. """ summary = getattr(execution_plan, "summary", {}) or {} summary_by_reason = getattr(execution_plan, "summary_by_reason", {}) or {} operations = getattr(execution_plan, "operations", []) or [] total = summary.get("total", len(operations)) parts = [ f"计划操作统计:共 {total} 条(move {summary.get('move', 0)},rename {summary.get('rename', 0)}," f"quarantine {summary.get('quarantine', 0)},no-op {summary.get('no-op', 0)})" ] if summary_by_reason: parts.append( "原因分布:" + ";".join(f"{reason}: {count}" for reason, count in list(summary_by_reason.items())[:8]) ) return "\n".join(parts) def preferred_plan_summary(execution_plan: Any) -> str: """Return human_summary if available, otherwise fallback summary.""" human_summary = getattr(execution_plan, "human_summary", "") if isinstance(human_summary, str) and human_summary.strip(): return human_summary return fallback_plan_summary(execution_plan) def render_review_preview( rows: list[dict[str, str]], preview_limit: int = 10, show_all: bool = False, ) -> tuple[list[str], int]: """Render high-risk review rows for console preview. Returns a tuple of (lines, remaining_count). """ if preview_limit < 1: raise ValueError("preview_limit must be >= 1") selected = rows if show_all else rows[:preview_limit] remaining = 0 if show_all else max(0, len(rows) - len(selected)) lines: list[str] = [] for row in selected: idx = row.get("index", "?") operation_type = row.get("operation_type", "") flags = row.get("risk_flags", "") or "none" source_path = row.get("source_path", "") destination_path = row.get("destination_path", "") reason = row.get("reason", "") lines.append(f" - [{idx}] {operation_type} | flags={flags}") lines.append(f" source: {source_path}") lines.append(f" destination: {destination_path or '(none)'}") lines.append(f" reason: {reason}") return lines, remaining