154 lines
5.0 KiB
Python
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
|