File size: 5,525 Bytes
34cb740
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""OMEGA CLI UI — Rich rendering helpers with graceful plain-text fallback."""

import os
from typing import Any, Dict, Optional, Sequence, Tuple

# Graceful import — fall back to plain text if Rich unavailable or NO_COLOR set
try:
    if os.environ.get("NO_COLOR"):
        raise ImportError("NO_COLOR")
    from rich.console import Console
    from rich.panel import Panel
    from rich.table import Table
    from rich.text import Text

    RICH_AVAILABLE = True
    console = Console()
except ImportError:
    RICH_AVAILABLE = False
    console = None  # type: ignore[assignment]


def print_header(title: str) -> None:
    """Print a styled header (Rich Panel or plain === title ===)."""
    if RICH_AVAILABLE:
        console.print(Panel(title, style="bold cyan", expand=False))
    else:
        print(f"\n=== {title} ===\n")


def print_section(title: str) -> None:
    """Print a section separator."""
    if RICH_AVAILABLE:
        console.print(f"\n[bold]{title}[/bold]")
        console.print("─" * min(len(title) + 4, 60), style="dim")
    else:
        print(f"\n--- {title} ---")


def print_kv(pairs: Sequence[Tuple[str, Any]], indent: int = 2) -> None:
    """Print key-value pairs with colored keys or plain text."""
    prefix = " " * indent
    if RICH_AVAILABLE:
        for key, value in pairs:
            console.print(f"{prefix}[bold cyan]{key}:[/bold cyan] {value}")
    else:
        for key, value in pairs:
            print(f"{prefix}{key}: {value}")


def print_table(
    title: Optional[str],
    columns: Sequence[str],
    rows: Sequence[Sequence[Any]],
    *,
    styles: Optional[Sequence[Optional[str]]] = None,
) -> None:
    """Print a formatted table (Rich Table or aligned plain text)."""
    if RICH_AVAILABLE:
        table = Table(title=title, show_lines=False, pad_edge=True)
        for i, col in enumerate(columns):
            style = styles[i] if styles and i < len(styles) else None
            table.add_column(col, style=style)
        for row in rows:
            table.add_row(*(str(cell) for cell in row))
        console.print(table)
    else:
        if title:
            print(f"\n{title}")
        if not rows:
            print("  (empty)")
            return
        # Calculate column widths
        widths = [len(str(c)) for c in columns]
        for row in rows:
            for i, cell in enumerate(row):
                if i < len(widths):
                    widths[i] = max(widths[i], len(str(cell)))
        # Header
        header = "  ".join(str(c).ljust(widths[i]) for i, c in enumerate(columns))
        print(f"  {header}")
        print(f"  {'  '.join('-' * w for w in widths)}")
        for row in rows:
            line = "  ".join(str(cell).ljust(widths[i]) for i, cell in enumerate(row) if i < len(widths))
            print(f"  {line}")


def print_bar_chart(
    items: Sequence[Tuple[str, int]],
    title: Optional[str] = None,
    total: Optional[int] = None,
) -> None:
    """Print a horizontal bar chart with colored blocks or ASCII #."""
    if total is None:
        total = sum(count for _, count in items)
    if total == 0:
        if title:
            print(f"  {title}: (no data)")
        return

    if RICH_AVAILABLE:
        table = Table(title=title, show_lines=False, pad_edge=True, show_header=True)
        table.add_column("Type", style="bold")
        table.add_column("Count", justify="right")
        table.add_column("%", justify="right")
        table.add_column("", min_width=25)

        colors = ["cyan", "green", "yellow", "magenta", "blue", "red", "white"]
        for i, (label, count) in enumerate(items):
            pct = count / total * 100
            bar_len = int(pct / 2)
            color = colors[i % len(colors)]
            bar = Text("█" * bar_len, style=color)
            table.add_row(label, str(count), f"{pct:.1f}%", bar)
        console.print(table)
    else:
        if title:
            print(f"\n{title}")
        for label, count in items:
            pct = count / total * 100
            bar = "#" * int(pct / 2)
            print(f"  {label:<20} {count:>5}  {pct:5.1f}%  {bar}")


_STATUS_SYMBOLS: Dict[str, Tuple[str, str]] = {
    "ok": ("  [bold green]✓[/bold green]", "  [OK]"),
    "fail": ("  [bold red]✗[/bold red]", "  [FAIL]"),
    "warn": ("  [bold yellow]![/bold yellow]", "  [WARN]"),
}


def print_status_line(status: str, msg: str) -> None:
    """Print a status line: green check / red X / yellow warning, or plain [OK]/[FAIL]/[WARN]."""
    rich_sym, plain_sym = _STATUS_SYMBOLS.get(status, ("  ?", "  [?]"))
    if RICH_AVAILABLE:
        console.print(f"{rich_sym} {msg}")
    else:
        print(f"{plain_sym} {msg}")


def print_summary(errors: int, warnings: int) -> None:
    """Print a final summary line."""
    if RICH_AVAILABLE:
        console.print("─" * 40, style="dim")
        if errors == 0 and warnings == 0:
            console.print("[bold green]All checks passed![/bold green]")
        elif errors == 0:
            console.print(f"[bold green]All checks passed[/bold green] with [yellow]{warnings} warning(s)[/yellow]")
        else:
            console.print(f"[bold red]{errors} error(s)[/bold red], [yellow]{warnings} warning(s)[/yellow]")
    else:
        print("=" * 40)
        if errors == 0 and warnings == 0:
            print("All checks passed!")
        elif errors == 0:
            print(f"All checks passed with {warnings} warning(s)")
        else:
            print(f"{errors} error(s), {warnings} warning(s)")