""" PyServe CLI Output utilities Rich-based formatters and helpers for CLI output. """ from rich.console import Console from rich.table import Table from rich.theme import Theme pyserve_theme = Theme( { "info": "cyan", "warning": "yellow", "error": "red bold", "success": "green", "service.running": "green", "service.stopped": "dim", "service.failed": "red", "service.starting": "yellow", } ) console = Console(theme=pyserve_theme) def print_error(message: str) -> None: console.print(f"[error] {message}[/error]") def print_warning(message: str) -> None: console.print(f"[warning] {message}[/warning]") def print_success(message: str) -> None: console.print(f"[success] {message}[/success]") def print_info(message: str) -> None: console.print(f"[info] {message}[/info]") def create_services_table() -> Table: table = Table( title=None, show_header=True, header_style="bold", border_style="dim", ) table.add_column("NAME", style="cyan", no_wrap=True) table.add_column("STATUS", no_wrap=True) table.add_column("PORTS", style="dim") table.add_column("UPTIME", style="dim") table.add_column("HEALTH", no_wrap=True) table.add_column("PID", style="dim") table.add_column("WORKERS", style="dim") return table def format_status(status: str) -> str: status_styles = { "running": "[service.running]● running[/service.running]", "stopped": "[service.stopped]○ stopped[/service.stopped]", "failed": "[service.failed]✗ failed[/service.failed]", "starting": "[service.starting]◐ starting[/service.starting]", "stopping": "[service.starting]◑ stopping[/service.starting]", "restarting": "[service.starting]↻ restarting[/service.starting]", "pending": "[service.stopped]○ pending[/service.stopped]", } return status_styles.get(status.lower(), status) def format_health(health: str) -> str: health_styles = { "healthy": "[green] healthy[/green]", "unhealthy": "[red] unhealthy[/red]", "degraded": "[yellow] degraded[/yellow]", "unknown": "[dim] unknown[/dim]", "-": "[dim]-[/dim]", } return health_styles.get(health.lower(), health) def format_uptime(seconds: float) -> str: if seconds <= 0: return "-" if seconds < 60: return f"{int(seconds)}s" elif seconds < 3600: minutes = int(seconds / 60) secs = int(seconds % 60) return f"{minutes}m {secs}s" elif seconds < 86400: hours = int(seconds / 3600) minutes = int((seconds % 3600) / 60) return f"{hours}h {minutes}m" else: days = int(seconds / 86400) hours = int((seconds % 86400) / 3600) return f"{days}d {hours}h" def format_bytes(num_bytes: int) -> str: value = float(num_bytes) for unit in ["B", "KB", "MB", "GB", "TB"]: if abs(value) < 1024.0: return f"{value:.1f}{unit}" value /= 1024.0 return f"{value:.1f}PB"