import click from pathlib import Path from rich.console import Console from rich.panel import Panel from rich.table import Table from rich.prompt import Prompt, Confirm from rich import print as rprint from typing import Optional, List from .models import HTTPRequest, HttpMethod, OutputFormat, InputFormat from .parsers import Parser from .generators import Generator from .highlighter import SyntaxHighlighter from .config import Config console = Console() def format_methods() -> str: return ", ".join([f"[bold]{m.value}[/bold]" for m in HttpMethod]) def format_output_formats() -> str: return ", ".join([f"[bold]{f.value}[/bold]" for f in OutputFormat]) @click.group() @click.option("--config", "-c", type=click.Path(), help="Path to config file") @click.pass_context def cli(ctx, config): ctx.ensure_object(dict) ctx.obj["config"] = Config() if not config else Config(Path(config)) @cli.command("convert") @click.argument("input_str", type=str) @click.option("--from", "-f", "input_format", type=click.Choice(["curl", "httpie", "fetch", "axios"]), help="Input format (auto-detected if not specified)") @click.option("--to", "-t", "output_format", type=click.Choice(["curl", "httpie", "fetch", "axios"]), required=True, help="Output format") @click.option("--compact/--no-compact", default=False, help="Compact output (no line breaks)") @click.option("--highlight/--no-highlight", default=None, help="Enable/disable syntax highlighting") @click.pass_context def convert(ctx, input_str, input_format, output_format, compact, highlight): config = ctx.obj.get("config", Config()) if highlight is None: highlight = config.syntax_highlighting try: if not input_format: input_format = Parser.detect_format(input_str) request = Parser.parse(input_str, input_format) output = Generator.generate(request, OutputFormat(output_format), compact=compact) console.print(Panel.fit( f"[bold]Input Format:[/bold] {input_format}\n" f"[bold]Output Format:[/bold] {output_format}\n" f"[bold]Method:[/bold] {request.method.value}\n" f"[bold]URL:[/bold] {request.url}", title="Request Details" )) if highlight: highlighted = SyntaxHighlighter.highlight(output, output_format) console.print(Panel.fit(highlighted, title=f"Converted to {output_format}", style="green")) else: console.print(Panel.fit(output, title=f"Converted to {output_format}", style="green")) except Exception as e: console.print(f"[bold red]Error:[/bold red] {str(e)}") raise click.Abort() @cli.command("interactive") @click.option("--format", "-f", "output_format", type=click.Choice(["curl", "httpie", "fetch", "axios"]), default="curl", help="Output format") @click.pass_context def interactive(ctx, output_format): config = ctx.obj.get("config", Config()) console.print(Panel.fit( "[bold cyan]HTTP Request Builder[/bold cyan]\n" "Let's build your HTTP request step by step.", title="Welcome" )) method_map = { "1": HttpMethod.GET, "2": HttpMethod.POST, "3": HttpMethod.PUT, "4": HttpMethod.PATCH, "5": HttpMethod.DELETE, } console.print("\n[bold]Select HTTP Method:[/bold]") for key, value in method_map.items(): console.print(f" {key}. {value.value}") method_choice = Prompt.ask("Choice", choices=list(method_map.keys()), default="1") method = method_map[method_choice] url = Prompt.ask("\n[bold]Enter URL[/bold] (e.g., https://api.example.com/users)") add_headers = Confirm.ask("\n[bold]Add headers?[/bold]", default=False) headers = {} if add_headers: console.print("\n[bold]Enter headers (press Enter on key to finish):[/bold]") while True: key = Prompt.ask(" Header name (or Enter to finish)", default="") if not key: break value = Prompt.ask(f" Value for '{key}'") headers[key] = value add_params = Confirm.ask("\n[bold]Add query parameters?[/bold]", default=False) params = {} if add_params: console.print("\n[bold]Enter query parameters (press Enter on key to finish):[/bold]") while True: key = Prompt.ask(" Parameter name (or Enter to finish)", default="") if not key: break value = Prompt.ask(f" Value for '{key}'") params[key] = value add_body = Confirm.ask("\n[bold]Add request body?[/bold]", default=False) body = None body_json = None if add_body: body_type = Confirm.ask(" Is it JSON?", default=True) if body_type: import json body_json_str = Prompt.ask(" Enter JSON string") try: body_json = json.loads(body_json_str) except json.JSONDecodeError as e: console.print(f"[yellow]Warning: Invalid JSON - {e}[/yellow]") body = body_json_str else: body = Prompt.ask(" Enter body content") request = HTTPRequest( method=method, url=url, headers=headers, params=params, body=body, body_json=body_json ) console.print("\n[bold]Request Preview:[/bold]") preview_table = Table() preview_table.add_column("Property", style="cyan") preview_table.add_column("Value", style="magenta") preview_table.add_row("Method", request.method.value) preview_table.add_row("URL", request.get_final_url()) for key, value in request.headers.items(): preview_table.add_row(f"Header: {key}", value) for key, value in request.params.items(): preview_table.add_row(f"Param: {key}", value) if request.body: preview_table.add_row("Body", request.body) if request.body_json: preview_table.add_row("Body (JSON)", str(request.body_json)) console.print(preview_table) output = Generator.generate(request, OutputFormat(output_format)) console.print(Panel.fit( SyntaxHighlighter.highlight(output, output_format), title=f"Generated {output_format}", style="green" )) if Confirm.ask("\n[bold]Convert to another format?[/bold]", default=False): available_formats = [f.value for f in OutputFormat if f.value != output_format] new_format = Prompt.ask("Choose format", choices=available_formats) new_output = Generator.generate(request, OutputFormat(new_format)) console.print(Panel.fit( SyntaxHighlighter.highlight(new_output, new_format), title=f"Converted to {new_format}", style="green" )) @cli.command("formats") def list_formats(): table = Table(title="Supported Formats") table.add_column("Format", style="cyan") table.add_column("Description", style="magenta") table.add_column("Example Start", style="yellow") formats_info = [ ("curl", "cURL command line tool", "curl -X POST https://..."), ("httpie", "HTTPie command line tool", "https://..."), ("fetch", "JavaScript Fetch API", "fetch('https://...')"), ("axios", "Axios HTTP client", "axios({ url: '...' })"), ] for fmt, desc, example in formats_info: table.add_row(fmt, desc, f"``{example}``") console.print(table) @cli.command("config") @click.option("--set-format", "set_format", type=click.Choice(["curl", "httpie", "fetch", "axios"]), help="Set default output format") @click.option("--toggle-highlight/--no-toggle-highlight", "toggle_highlight", default=None, help="Toggle syntax highlighting") @click.option("--toggle-compact/--no-toggle-compact", "toggle_compact", default=None, help="Toggle compact output") @click.option("--show", is_flag=True, help="Show current configuration") @click.option("--reset", is_flag=True, help="Reset to defaults") @click.pass_context def config_cmd(ctx, set_format, toggle_highlight, toggle_compact, show, reset): config = ctx.obj.get("config", Config()) if reset: config.reset() console.print("[green]Configuration reset to defaults[/green]") return if set_format: config.default_format = set_format console.print(f"[green]Default format set to: {set_format}[/green]") if toggle_highlight is not None: config.syntax_highlighting = toggle_highlight console.print(f"[green]Syntax highlighting: {'enabled' if toggle_highlight else 'disabled'}[/green]") if toggle_compact is not None: config.compact_output = toggle_compact console.print(f"[green]Compact output: {'enabled' if toggle_compact else 'disabled'}[/green]") if show or not any([set_format, toggle_highlight is not None, toggle_compact is not None]): table = Table(title="Current Configuration") table.add_column("Setting", style="cyan") table.add_column("Value", style="magenta") table.add_row("Default Format", config.default_format) table.add_row("Syntax Highlighting", "Enabled" if config.syntax_highlighting else "Disabled") table.add_row("Compact Output", "Enabled" if config.compact_output else "Disabled") table.add_row("Theme", config.theme) console.print(table) @cli.command("web") @click.option("--host", default="127.0.0.1", help="Host to bind to") @click.option("--port", default=8000, type=int, help="Port to bind to") @click.option("--open/--no-open", default=True, help="Open browser automatically") def web(host, port, open): from .web import app import uvicorn import webbrowser import threading url = f"http://{host}:{port}" if open: def open_browser(): webbrowser.open(url) threading.Timer(1.0, open_browser).start() console.print(Panel.fit( f"[bold green]Web Interface[/bold green]\n" f"Open your browser at: [cyan]{url}[/cyan]\n" f"Press Ctrl+C to stop the server", title="HTTP Convert" )) uvicorn.run(app, host=host, port=port) @cli.command("version") def version(): from . import __version__ console.print(f"[bold]HTTP Convert[/bold] v{__version__}") def main(): cli(obj={}) if __name__ == "__main__": main()