Files
schema2mock/http_log_explorer/cli/formatter.py

154 lines
5.0 KiB
Python

"""Rich table formatter for HTTP entries."""
from typing import Any
from rich.console import Console
from rich.table import Table
from rich.text import Text
from http_log_explorer.models import HTTPEntry
class Formatter:
"""Format HTTP entries for terminal display."""
def __init__(self) -> None:
"""Initialize formatter."""
self.console = Console()
def format_entry_table(
self,
entries: list[HTTPEntry],
show_headers: bool = True,
show_body: bool = False,
limit: int | None = None,
) -> Table:
"""Create a table of HTTP entries.
Args:
entries: List of HTTPEntry objects
show_headers: Whether to show request/response headers
show_body: Whether to show request/response body
limit: Maximum number of entries to show
Returns:
Rich Table object
"""
table = Table(title=f"HTTP Entries ({len(entries)} total)")
table.add_column("ID", style="cyan", no_wrap=True)
table.add_column("Method", style="magenta", no_wrap=True)
table.add_column("URL", style="blue")
table.add_column("Status", justify="center", no_wrap=True)
table.add_column("Time", style="dim", no_wrap=True)
table.add_column("Duration", justify="right", no_wrap=True)
if show_headers:
table.add_column("Req Headers", style="dim")
table.add_column("Resp Headers", style="dim")
if show_body:
table.add_column("Req Body", style="dim")
table.add_column("Resp Body", style="dim")
display_entries = entries[:limit] if limit else entries
for entry in display_entries:
row: list[Any] = [
entry.id,
entry.request.method,
self._truncate_url(entry.request.url),
self._format_status(entry.response.status),
self._format_timestamp(entry.timestamp),
self._format_duration(entry.duration_ms),
]
if show_headers:
row.append(self._format_headers(entry.request.headers))
row.append(self._format_headers(entry.response.headers))
if show_body:
row.append(self._truncate_body(entry.request.body))
row.append(self._truncate_body(entry.response.body))
table.add_row(*row)
return table
def _truncate_url(self, url: str, max_length: int = 60) -> str:
"""Truncate URL for display."""
if len(url) <= max_length:
return url
return url[: max_length - 3] + "..."
def _format_status(self, status: int) -> Text:
"""Format status code with color."""
if 200 <= status < 300:
return Text(str(status), style="green")
elif 300 <= status < 400:
return Text(str(status), style="blue")
elif 400 <= status < 500:
return Text(str(status), style="yellow")
elif 500 <= status < 600:
return Text(str(status), style="red")
return Text(str(status))
def _format_timestamp(self, timestamp: Any) -> str:
"""Format timestamp for display."""
if timestamp is None:
return "-"
if hasattr(timestamp, "strftime"):
return timestamp.strftime("%H:%M:%S")
return str(timestamp)
def _format_duration(self, duration_ms: float | None) -> str:
"""Format duration for display."""
if duration_ms is None:
return "-"
if duration_ms < 1000:
return f"{duration_ms:.0f}ms"
return f"{duration_ms / 1000:.2f}s"
def _format_headers(self, headers: dict[str, str]) -> str:
"""Format headers for display."""
if not headers:
return "-"
count = len(headers)
return f"{count} headers"
def _truncate_body(self, body: str | None, max_length: int = 50) -> str:
"""Truncate body for display."""
if body is None:
return "-"
body = body.strip()
if not body:
return "-"
if len(body) <= max_length:
return body
return body[: max_length - 3] + "..."
def format_diff(self, diff_output: str) -> Table:
"""Format diff output as table.
Args:
diff_output: Diff output string
Returns:
Rich Table object
"""
table = Table(title="Diff Comparison")
table.add_column("Before/After", style="cyan", no_wrap=True)
table.add_column("Change", style="white")
for line in diff_output.split("\n"):
if line.startswith("-"):
table.add_row("-", Text(line, style="red"))
elif line.startswith("+"):
table.add_row("+", Text(line, style="green"))
elif line.startswith("---"):
table.add_row("", Text(line, style="dim"))
else:
table.add_row("", line)
return table