Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ba975ffed4 | |||
| cec136d7e1 | |||
| cdf9436a2e | |||
| 527c79e3cd | |||
| 658f5b0d7e | |||
| 55d9312f51 | |||
| efb912c3dc | |||
| d0a44ea8b2 | |||
| bc3dd3c0cf | |||
| 28f698de11 | |||
| 13968ae8ad | |||
| 1a3ec64292 |
@@ -9,20 +9,26 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
- uses: actions/setup-python@v5
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v5
|
||||||
with:
|
with:
|
||||||
python-version: '3.11'
|
python-version: '3.11'
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip install -e ".[dev]"
|
pip install click>=8.0 requests>=2.28 pyyaml>=6.0
|
||||||
|
pip install pytest pytest-cov
|
||||||
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: pytest tests/ -v
|
run: |
|
||||||
|
python -m pytest tests/ -v
|
||||||
|
|
||||||
- name: Check code style (ruff)
|
- name: Check linting
|
||||||
run: pip install ruff && ruff check .
|
run: |
|
||||||
|
pip install ruff
|
||||||
|
python -m ruff check .
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
"""Curl Converter CLI - Convert curl commands to code snippets."""
|
{"""Curl Converter CLI - Convert curl commands to code."""
|
||||||
|
|
||||||
__version__ = "0.1.0"
|
__version__ = "0.1.0"
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
"""CLI module for curl converter."""
|
{"""CLI application for curl-converter."""
|
||||||
|
|
||||||
import sys
|
|
||||||
import click
|
import click
|
||||||
from curlconverter.parser import parse_curl
|
from curlconverter.parser import parse_curl
|
||||||
from curlconverter.generators import generate_code, get_supported_languages, get_language_display_name
|
from curlconverter.generators import generate_code, get_supported_languages, get_language_display_name
|
||||||
@@ -8,98 +7,63 @@ from curlconverter.generators import generate_code, get_supported_languages, get
|
|||||||
|
|
||||||
@click.group()
|
@click.group()
|
||||||
def cli():
|
def cli():
|
||||||
"""Convert curl commands to code in multiple programming languages."""
|
"""Curl Converter CLI - Convert curl commands to code."""
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option("-c", "--curl", "curl_input", help="The curl command to convert")
|
@click.option('--curl', '-c', help='Curl command to convert')
|
||||||
@click.option("-l", "--language", default="python", help="Target programming language")
|
@click.option('--language', '-l', default='python', help='Target language')
|
||||||
@click.option("-o", "--output", type=click.Path(), help="Output file path")
|
@click.option('--output', '-o', type=click.Path(), help='Output file')
|
||||||
@click.option("-i", "--interactive", is_flag=True, help="Interactive mode - paste curl command")
|
def convert(curl, language, output):
|
||||||
def convert(curl_input, language, output, interactive):
|
|
||||||
"""Convert a curl command to code."""
|
"""Convert a curl command to code."""
|
||||||
if interactive:
|
if not curl:
|
||||||
click.echo("Paste your curl command (press Ctrl+D or Ctrl+Z when done):")
|
curl = click.prompt('Enter curl command', type=str)
|
||||||
try:
|
|
||||||
curl_input = click.edit()
|
|
||||||
except Exception:
|
|
||||||
curl_input = click.prompt("", type=str)
|
|
||||||
|
|
||||||
if not curl_input:
|
parsed = parse_curl(curl)
|
||||||
click.echo("Error: No curl command provided. Use --curl or --interactive", err=True)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
try:
|
|
||||||
parsed = parse_curl(curl_input)
|
|
||||||
code = generate_code(parsed, language)
|
code = generate_code(parsed, language)
|
||||||
|
|
||||||
if output:
|
if output:
|
||||||
with open(output, "w") as f:
|
with open(output, 'w') as f:
|
||||||
f.write(code)
|
f.write(code)
|
||||||
click.echo(f"Code written to {output}")
|
click.echo(f'Code written to {output}')
|
||||||
else:
|
else:
|
||||||
click.echo(code)
|
click.echo(code)
|
||||||
|
|
||||||
except ValueError as e:
|
|
||||||
click.echo(f"Error: {e}", err=True)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
def languages():
|
def languages():
|
||||||
"""List supported programming languages."""
|
"""List supported languages."""
|
||||||
click.echo("Supported languages:")
|
|
||||||
for lang in get_supported_languages():
|
for lang in get_supported_languages():
|
||||||
display = get_language_display_name(lang)
|
click.echo(f"{lang}: {get_language_display_name(lang)}")
|
||||||
click.echo(f" {lang:12} - {display}")
|
|
||||||
|
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.option("-c", "--curl", "curl_input", help="The curl command to analyze")
|
@click.argument('curl')
|
||||||
@click.option("-i", "--interactive", is_flag=True, help="Interactive mode")
|
def analyze(curl):
|
||||||
def analyze(curl_input, interactive):
|
"""Analyze a curl command without generating code."""
|
||||||
"""Analyze and display parsed curl command details."""
|
parsed = parse_curl(curl)
|
||||||
if interactive:
|
|
||||||
click.echo("Paste your curl command:")
|
|
||||||
curl_input = click.prompt("", type=str)
|
|
||||||
|
|
||||||
if not curl_input:
|
click.echo(f"URL: {parsed.url}")
|
||||||
click.echo("Error: No curl command provided", err=True)
|
click.echo(f"Method: {parsed.method}")
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
try:
|
|
||||||
parsed = parse_curl(curl_input)
|
|
||||||
|
|
||||||
click.echo("Parsed curl command:")
|
|
||||||
click.echo(f" URL: {parsed.url}")
|
|
||||||
click.echo(f" Method: {parsed.method}")
|
|
||||||
|
|
||||||
if parsed.headers:
|
if parsed.headers:
|
||||||
click.echo(" Headers:")
|
click.echo("Headers:")
|
||||||
for k, v in parsed.headers.items():
|
for k, v in parsed.headers.items():
|
||||||
click.echo(f" {k}: {v}")
|
click.echo(f" {k}: {v}")
|
||||||
|
|
||||||
if parsed.data:
|
if parsed.data:
|
||||||
click.echo(f" Data: {parsed.data}")
|
click.echo(f"Data: {parsed.data}")
|
||||||
|
|
||||||
if parsed.auth:
|
if parsed.auth:
|
||||||
click.echo(f" Auth: {parsed.auth[0]}:****")
|
click.echo(f"Auth: {parsed.auth}")
|
||||||
|
|
||||||
if parsed.cookies:
|
if parsed.cookies:
|
||||||
click.echo(f" Cookies: {parsed.cookies}")
|
click.echo(f"Cookies: {parsed.cookies}")
|
||||||
|
|
||||||
if parsed.user_agent:
|
if parsed.user_agent:
|
||||||
click.echo(f" User-Agent: {parsed.user_agent}")
|
click.echo(f"User-Agent: {parsed.user_agent}")
|
||||||
|
|
||||||
except ValueError as e:
|
|
||||||
click.echo(f"Error: {e}", err=True)
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
if __name__ == '__main__':
|
||||||
cli()
|
cli()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
"""Code generators for different programming languages."""
|
{"""Code generators for different programming languages."""
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from typing import Dict, Callable
|
from typing import Dict, Callable
|
||||||
|
|
||||||
from curlconverter.parser import ParsedCurl
|
from curlconverter.parser import ParsedCurl
|
||||||
|
|
||||||
|
|
||||||
@@ -57,8 +58,8 @@ def get_language_display_name(lang: str) -> str:
|
|||||||
return names.get(lang.lower(), lang.capitalize())
|
return names.get(lang.lower(), lang.capitalize())
|
||||||
|
|
||||||
|
|
||||||
from curlconverter.generators import python
|
from curlconverter.generators import python # noqa: E402, F401
|
||||||
from curlconverter.generators import javascript
|
from curlconverter.generators import javascript # noqa: E402, F401
|
||||||
from curlconverter.generators import go
|
from curlconverter.generators import go # noqa: E402, F401
|
||||||
from curlconverter.generators import ruby
|
from curlconverter.generators import ruby # noqa: E402, F401
|
||||||
from curlconverter.generators import php
|
from curlconverter.generators import php # noqa: E402, F401
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
"""Go code generator."""
|
{"""Go code generator."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
from curlconverter.parser import ParsedCurl
|
from curlconverter.parser import ParsedCurl
|
||||||
from curlconverter.generators import register_generator
|
from curlconverter.generators import register_generator
|
||||||
|
|
||||||
@@ -24,18 +25,19 @@ def _detect_content_type(headers: dict, data: str) -> str:
|
|||||||
def generate_go(parsed: ParsedCurl) -> str:
|
def generate_go(parsed: ParsedCurl) -> str:
|
||||||
"""Generate Go net/http code from parsed curl data."""
|
"""Generate Go net/http code from parsed curl data."""
|
||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
lines.append("package main")
|
lines.append("package main")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append('import (')
|
lines.append("import (")
|
||||||
lines.append(' "bytes"')
|
lines.append(' "bytes"')
|
||||||
lines.append(' "fmt"')
|
lines.append(' "fmt"')
|
||||||
lines.append(' "net/http"')
|
lines.append(' "net/http"')
|
||||||
lines.append(' "io/ioutil"')
|
lines.append(' "strings"')
|
||||||
lines.append(")")
|
lines.append(")")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
lines.append("func main() {")
|
lines.append("func main() {")
|
||||||
lines.append(f' url := "{parsed.url}"')
|
lines.append(f' url := {repr(parsed.url)}')
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
headers = dict(parsed.headers) if parsed.headers else {}
|
headers = dict(parsed.headers) if parsed.headers else {}
|
||||||
@@ -43,57 +45,50 @@ def generate_go(parsed: ParsedCurl) -> str:
|
|||||||
if parsed.user_agent and "User-Agent" not in headers and "user-agent" not in headers:
|
if parsed.user_agent and "User-Agent" not in headers and "user-agent" not in headers:
|
||||||
headers["User-Agent"] = parsed.user_agent
|
headers["User-Agent"] = parsed.user_agent
|
||||||
|
|
||||||
if headers:
|
if parsed.auth:
|
||||||
lines.append(" headers := map[string]string{")
|
auth_str = f"{parsed.auth[0]}:{parsed.auth[1]}"
|
||||||
for k, v in headers.items():
|
encoded = ""
|
||||||
lines.append(f' "{k}": "{v}",')
|
for _, c := range auth_str {
|
||||||
lines.append(" }")
|
encoded += string(c)
|
||||||
lines.append("")
|
}
|
||||||
|
headers["Authorization"] = f"Basic " + encoded
|
||||||
|
|
||||||
|
method = parsed.method
|
||||||
|
body := ""
|
||||||
|
|
||||||
body_var = ""
|
|
||||||
if parsed.data:
|
if parsed.data:
|
||||||
content_type = _detect_content_type(headers, parsed.data)
|
content_type = _detect_content_type(headers, parsed.data)
|
||||||
|
body = repr(parsed.data)
|
||||||
|
|
||||||
if content_type == "application/json":
|
if content_type == "application/json":
|
||||||
try:
|
try:
|
||||||
json_data = json.loads(parsed.data)
|
json_data = json.loads(parsed.data)
|
||||||
json_str = json.dumps(json_data)
|
body = "strings.NewReader(" + repr(json.dumps(json_data)) + ")"
|
||||||
lines.append(f' jsonData := `{json_str}`')
|
headers["Content-Type"] = "application/json"
|
||||||
body_var = "bytes.NewBuffer([]byte(jsonData))"
|
|
||||||
except (json.JSONDecodeError, TypeError):
|
except (json.JSONDecodeError, TypeError):
|
||||||
escaped = parsed.data.replace("`", "\\`").replace("${", "\\$ {")
|
body = "strings.NewReader(" + repr(parsed.data) + ")"
|
||||||
lines.append(f' data := `{escaped}`')
|
|
||||||
body_var = "bytes.NewBuffer([]byte(data))"
|
|
||||||
else:
|
else:
|
||||||
escaped = parsed.data.replace("`", "\\`").replace("${", "\\$ {")
|
body = "strings.NewReader(" + repr(parsed.data) + ")"
|
||||||
lines.append(f' data := `{escaped}`')
|
|
||||||
body_var = "bytes.NewBuffer([]byte(data))"
|
|
||||||
|
|
||||||
if "Content-Type" not in headers and "content-type" not in headers:
|
if method == "GET":
|
||||||
headers["Content-Type"] = content_type
|
method = "POST"
|
||||||
|
|
||||||
lines.append("")
|
if headers:
|
||||||
|
lines.append(" req, err := http.NewRequest(" + repr(method) + ", url, " + body + ")")
|
||||||
lines.append(f' req, err := http.NewRequest("{parsed.method}", url, {body_var or "nil"})')
|
|
||||||
lines.append(" if err != nil {")
|
lines.append(" if err != nil {")
|
||||||
lines.append(" panic(err)")
|
lines.append(" panic(err)")
|
||||||
lines.append(" }")
|
lines.append(" }")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
if headers:
|
for k, v in headers.items():
|
||||||
lines.append(" for key, value := range headers {")
|
lines.append(f' req.Header.Add({repr(k)}, {repr(v)})')
|
||||||
lines.append(" req.Header.Add(key, value)")
|
else:
|
||||||
|
lines.append(" req, err := http.NewRequest(" + repr(method) + ", url, " + body + ")")
|
||||||
|
lines.append(" if err != nil {")
|
||||||
|
lines.append(" panic(err)")
|
||||||
lines.append(" }")
|
lines.append(" }")
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
if parsed.auth:
|
|
||||||
lines.append(f' req.SetBasicAuth("{parsed.auth[0]}", "{parsed.auth[1]}")')
|
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
if parsed.cookies:
|
|
||||||
lines.append(f' req.Header.Add("Cookie", "{parsed.cookies}")')
|
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
lines.append(" client := &http.Client{}")
|
lines.append(" client := &http.Client{}")
|
||||||
lines.append(" resp, err := client.Do(req)")
|
lines.append(" resp, err := client.Do(req)")
|
||||||
lines.append(" if err != nil {")
|
lines.append(" if err != nil {")
|
||||||
@@ -101,8 +96,7 @@ def generate_go(parsed: ParsedCurl) -> str:
|
|||||||
lines.append(" }")
|
lines.append(" }")
|
||||||
lines.append(" defer resp.Body.Close()")
|
lines.append(" defer resp.Body.Close()")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append(" body, _ := ioutil.ReadAll(resp.Body)")
|
lines.append(" fmt.Println(resp.Status)")
|
||||||
lines.append(" fmt.Println(string(body))")
|
|
||||||
lines.append("}")
|
lines.append("}")
|
||||||
|
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
"""JavaScript code generator."""
|
{"""JavaScript code generator."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from curlconverter.parser import ParsedCurl
|
from curlconverter.parser import ParsedCurl
|
||||||
@@ -24,7 +24,8 @@ def _detect_content_type(headers: dict, data: str) -> str:
|
|||||||
def generate_javascript(parsed: ParsedCurl) -> str:
|
def generate_javascript(parsed: ParsedCurl) -> str:
|
||||||
"""Generate JavaScript fetch code from parsed curl data."""
|
"""Generate JavaScript fetch code from parsed curl data."""
|
||||||
lines = []
|
lines = []
|
||||||
lines.append("const url = " + repr(parsed.url) + ";")
|
|
||||||
|
lines.append(f"const url = {repr(parsed.url)};")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
options = {"method": parsed.method}
|
options = {"method": parsed.method}
|
||||||
@@ -33,46 +34,37 @@ def generate_javascript(parsed: ParsedCurl) -> str:
|
|||||||
if parsed.user_agent and "User-Agent" not in headers and "user-agent" not in headers:
|
if parsed.user_agent and "User-Agent" not in headers and "user-agent" not in headers:
|
||||||
headers["User-Agent"] = parsed.user_agent
|
headers["User-Agent"] = parsed.user_agent
|
||||||
|
|
||||||
|
if parsed.auth:
|
||||||
|
encoded = btoa(f"{parsed.auth[0]}:{parsed.auth[1]}")
|
||||||
|
headers["Authorization"] = f"Basic {encoded}"
|
||||||
|
|
||||||
if parsed.data:
|
if parsed.data:
|
||||||
content_type = _detect_content_type(headers, parsed.data)
|
content_type = _detect_content_type(headers, parsed.data)
|
||||||
|
|
||||||
if content_type == "application/json":
|
if content_type == "application/json":
|
||||||
try:
|
try:
|
||||||
json_data = json.loads(parsed.data)
|
json_data = json.loads(parsed.data)
|
||||||
lines.append("const jsonData = " + json.dumps(json_data, indent=2) + ";")
|
options["body"] = json.dumps(json_data)
|
||||||
options["body"] = "JSON.stringify(jsonData)"
|
|
||||||
if "Content-Type" not in headers and "content-type" not in headers:
|
|
||||||
headers["Content-Type"] = "application/json"
|
headers["Content-Type"] = "application/json"
|
||||||
except (json.JSONDecodeError, TypeError):
|
except (json.JSONDecodeError, TypeError):
|
||||||
lines.append("const data = " + repr(parsed.data) + ";")
|
options["body"] = repr(parsed.data)
|
||||||
options["body"] = "data"
|
|
||||||
else:
|
else:
|
||||||
lines.append("const data = " + repr(parsed.data) + ";")
|
options["body"] = repr(parsed.data)
|
||||||
options["body"] = "data"
|
|
||||||
|
|
||||||
if parsed.cookies:
|
if parsed.cookies:
|
||||||
if "Cookie" not in headers and "cookie" not in headers:
|
|
||||||
headers["Cookie"] = parsed.cookies
|
headers["Cookie"] = parsed.cookies
|
||||||
|
|
||||||
if headers:
|
if headers:
|
||||||
lines.append("const headers = " + _format_object(headers) + ";")
|
lines.append("const headers = " + json.dumps(headers, indent=2) + ";")
|
||||||
options["headers"] = "headers"
|
options["headers"] = "headers"
|
||||||
|
|
||||||
if parsed.auth:
|
if len(options) > 1:
|
||||||
auth_str = f"{parsed.auth[0]}:{parsed.auth[1]}"
|
lines.append("const options = " + json.dumps(options, indent=2) + ";")
|
||||||
import base64
|
|
||||||
encoded = base64.b64encode(auth_str.encode()).decode()
|
|
||||||
lines.append("const auth = " + repr(encoded) + ";")
|
|
||||||
if "Authorization" not in headers and "content-type" not in headers:
|
|
||||||
lines.append("const headersWithAuth = {")
|
|
||||||
lines.append(" ...headers,")
|
|
||||||
lines.append(' "Authorization": `Basic ${auth}`')
|
|
||||||
lines.append("};")
|
|
||||||
options["headers"] = "headersWithAuth"
|
|
||||||
|
|
||||||
options_str = _format_options(options)
|
|
||||||
lines.append("")
|
lines.append("")
|
||||||
lines.append(f"fetch(url, {options_str})")
|
lines.append("fetch(url, options)")
|
||||||
|
else:
|
||||||
|
lines.append("fetch(url)")
|
||||||
|
|
||||||
lines.append(" .then(response => {")
|
lines.append(" .then(response => {")
|
||||||
lines.append(" console.log(response.status);")
|
lines.append(" console.log(response.status);")
|
||||||
lines.append(" return response.text();")
|
lines.append(" return response.text();")
|
||||||
@@ -81,23 +73,3 @@ def generate_javascript(parsed: ParsedCurl) -> str:
|
|||||||
lines.append(" .catch(error => console.error(error));")
|
lines.append(" .catch(error => console.error(error));")
|
||||||
|
|
||||||
return "\n".join(lines)
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
def _format_object(obj: dict) -> str:
|
|
||||||
"""Format an object for JavaScript code."""
|
|
||||||
if not obj:
|
|
||||||
return "{}"
|
|
||||||
items = []
|
|
||||||
for k, v in obj.items():
|
|
||||||
items.append(f' {repr(k)}: {repr(v)}')
|
|
||||||
return "{\n" + ",\n".join(items) + "\n}"
|
|
||||||
|
|
||||||
|
|
||||||
def _format_options(opts: dict) -> str:
|
|
||||||
"""Format fetch options for JavaScript code."""
|
|
||||||
if not opts:
|
|
||||||
return "{}"
|
|
||||||
items = []
|
|
||||||
for k, v in opts.items():
|
|
||||||
items.append(f" {k}: {v}")
|
|
||||||
return "{\n" + ",\n".join(items) + "\n }"
|
|
||||||
|
|||||||
90
curlconverter/generators/php.php
Normal file
90
curlconverter/generators/php.php
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
{"""PHP code generator."""
|
||||||
|
|
||||||
|
import json
|
||||||
|
|
||||||
|
from curlconverter.parser import ParsedCurl
|
||||||
|
from curlconverter.generators import register_generator
|
||||||
|
|
||||||
|
|
||||||
|
def _detect_content_type(headers: dict, data: str) -> str:
|
||||||
|
"""Detect content type from headers or data."""
|
||||||
|
if "Content-Type" in headers:
|
||||||
|
return headers["Content-Type"]
|
||||||
|
if "content-type" in headers:
|
||||||
|
return headers["content-type"]
|
||||||
|
if data:
|
||||||
|
try:
|
||||||
|
json.loads(data)
|
||||||
|
return "application/json"
|
||||||
|
except (json.JSONDecodeError, TypeError):
|
||||||
|
pass
|
||||||
|
return "application/x-www-form-urlencoded"
|
||||||
|
|
||||||
|
|
||||||
|
@register_generator("php")
|
||||||
|
def generate_php(parsed: ParsedCurl) -> str:
|
||||||
|
"""Generate PHP cURL code from parsed curl data."""
|
||||||
|
lines = []
|
||||||
|
lines.append("<?php")
|
||||||
|
lines.append("")
|
||||||
|
lines.append("$url = " + repr(parsed.url) + ";")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines.append("$ch = curl_init();")
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
lines.append('curl_setopt($ch, CURLOPT_URL, $url);')
|
||||||
|
lines.append('curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);')
|
||||||
|
lines.append(f'curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "{parsed.method}");')
|
||||||
|
lines.append("")
|
||||||
|
|
||||||
|
headers = dict(parsed.headers) if parsed.headers else {}
|
||||||
|
|
||||||
|
if parsed.user_agent and "User-Agent" not in headers and "user-agent" not in headers:
|
||||||
|
headers["User-Agent"] = parsed.user_agent
|
||||||
|
|
||||||
|
if parsed.data:
|
||||||
|
content_type = _detect_content_type(headers, parsed.data)
|
||||||
|
|
||||||
|
if content_type == "application/json":
|
||||||
|
try:
|
||||||
|
json_data = json.loads(parsed.data)
|
||||||
|
lines.append("$data = " + repr(json.dumps(json_data)) + ";")
|
||||||
|
lines.append("curl_setopt($ch, CURLOPT_POSTFIELDS, $data);")
|
||||||
|
lines.append('curl_setopt($ch, CURLOPT_HTTPHEADER, array(\'Content-Type: application/json\'));')
|
||||||
|
except (json.JSONDecodeError, TypeError):
|
||||||
|
lines.append("$data = " + repr(parsed.data) + ";")
|
||||||
|
lines.append("curl_setopt($ch, CURLOPT_POSTFIELDS, $data);")
|
||||||
|
else:
|
||||||
|
lines.append("$data = " + repr(parsed.data) + ";")
|
||||||
|
lines.append("curl_setopt($ch, CURLOPT_POSTFIELDS, $data);")
|
||||||
|
|
||||||
|
if parsed.method == "GET":
|
||||||
|
lines.append("curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: ' . $content_type));")
|
||||||
|
|
||||||
|
if parsed.auth:
|
||||||
|
lines.append(f'$auth = base64_encode("{parsed.auth[0]}:{parsed.auth[1]}");')
|
||||||
|
lines.append('curl_setopt($ch, CURLOPT_HTTPHEADER, array(\'Authorization: Basic \' . $auth));')
|
||||||
|
|
||||||
|
if headers:
|
||||||
|
http_headers = []
|
||||||
|
for k, v in headers.items():
|
||||||
|
http_headers.append(f"{k}: {v}")
|
||||||
|
lines.append('curl_setopt($ch, CURLOPT_HTTPHEADER, ' + repr(http_headers) + ');')
|
||||||
|
|
||||||
|
lines.append("$response = curl_exec($ch);")
|
||||||
|
lines.append("")
|
||||||
|
lines.append("if (curl_errno($ch)) {")
|
||||||
|
lines.append(" echo 'Error:' . curl_error($ch);")
|
||||||
|
lines.append("}")
|
||||||
|
lines.append("")
|
||||||
|
lines.append("curl_close($ch);")
|
||||||
|
lines.append("echo $response;")
|
||||||
|
|
||||||
|
return "\n".join(lines)
|
||||||
|
|
||||||
|
|
||||||
|
import curlconverter.generators.python # noqa: E402, F401
|
||||||
|
import curlconverter.generators.javascript # noqa: E402, F401
|
||||||
|
import curlconverter.generators.go # noqa: E402, F401
|
||||||
|
import curlconverter.generators.ruby # noqa: E402, F401
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Python code generator."""
|
{"""Python code generator."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import base64
|
import base64
|
||||||
@@ -40,18 +40,14 @@ def generate_python(parsed: ParsedCurl) -> str:
|
|||||||
lines.append("headers = " + _format_dict(headers))
|
lines.append("headers = " + _format_dict(headers))
|
||||||
kwargs.append("headers=headers")
|
kwargs.append("headers=headers")
|
||||||
|
|
||||||
auth_tuple = None
|
|
||||||
if parsed.auth:
|
if parsed.auth:
|
||||||
auth_username, auth_password = parsed.auth
|
|
||||||
if ":" in parsed.auth[0]:
|
if ":" in parsed.auth[0]:
|
||||||
try:
|
try:
|
||||||
encoded = base64.b64encode(f"{parsed.auth[0]}:{parsed.auth[1]}".encode()).decode()
|
encoded = base64.b64encode(f"{parsed.auth[0]}:{parsed.auth[1]}".encode()).decode()
|
||||||
if "Authorization" not in headers:
|
if "Authorization" not in headers:
|
||||||
headers["Authorization"] = f"Basic {encoded}"
|
headers["Authorization"] = f"Basic {encoded}"
|
||||||
except Exception:
|
except Exception:
|
||||||
auth_tuple = parsed.auth
|
pass
|
||||||
else:
|
|
||||||
auth_tuple = parsed.auth
|
|
||||||
|
|
||||||
if parsed.data:
|
if parsed.data:
|
||||||
content_type = _detect_content_type(headers, parsed.data)
|
content_type = _detect_content_type(headers, parsed.data)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Ruby code generator."""
|
{"""Ruby code generator."""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
from curlconverter.parser import ParsedCurl
|
from curlconverter.parser import ParsedCurl
|
||||||
@@ -24,16 +24,14 @@ def _detect_content_type(headers: dict, data: str) -> str:
|
|||||||
def generate_ruby(parsed: ParsedCurl) -> str:
|
def generate_ruby(parsed: ParsedCurl) -> str:
|
||||||
"""Generate Ruby Net::HTTP code from parsed curl data."""
|
"""Generate Ruby Net::HTTP code from parsed curl data."""
|
||||||
lines = []
|
lines = []
|
||||||
|
|
||||||
lines.append("require 'net/http'")
|
lines.append("require 'net/http'")
|
||||||
lines.append("require 'uri'")
|
lines.append("require 'uri'")
|
||||||
lines.append("require 'json'")
|
lines.append("require 'json'")
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
lines.append("url = URI(" + repr(parsed.url) + ")")
|
lines.append(f"url = URI({repr(parsed.url)})")
|
||||||
lines.append("")
|
|
||||||
|
|
||||||
lines.append("http = Net::HTTP.new(url.host, url.port)")
|
lines.append("http = Net::HTTP.new(url.host, url.port)")
|
||||||
lines.append("http.use_ssl = true")
|
|
||||||
lines.append("")
|
lines.append("")
|
||||||
|
|
||||||
headers = dict(parsed.headers) if parsed.headers else {}
|
headers = dict(parsed.headers) if parsed.headers else {}
|
||||||
@@ -42,11 +40,11 @@ def generate_ruby(parsed: ParsedCurl) -> str:
|
|||||||
headers["User-Agent"] = parsed.user_agent
|
headers["User-Agent"] = parsed.user_agent
|
||||||
|
|
||||||
method_map = {
|
method_map = {
|
||||||
"GET": "Get",
|
"GET" => "Get",
|
||||||
"POST": "Post",
|
"POST" => "Post",
|
||||||
"PUT": "Put",
|
"PUT" => "Put",
|
||||||
"DELETE": "Delete",
|
"DELETE" => "Delete",
|
||||||
"PATCH": "Patch"
|
"PATCH" => "Patch"
|
||||||
}
|
}
|
||||||
request_class = f"Net::HTTP::{method_map.get(parsed.method, parsed.method)}"
|
request_class = f"Net::HTTP::{method_map.get(parsed.method, parsed.method)}"
|
||||||
if parsed.data:
|
if parsed.data:
|
||||||
@@ -56,13 +54,13 @@ def generate_ruby(parsed: ParsedCurl) -> str:
|
|||||||
try:
|
try:
|
||||||
json_data = json.loads(parsed.data)
|
json_data = json.loads(parsed.data)
|
||||||
lines.append("data = " + json.dumps(json_data))
|
lines.append("data = " + json.dumps(json_data))
|
||||||
request_str = f"Net::HTTP::Post.new(url, {{'Content-Type' => 'application/json'}})"
|
request_str = "Net::HTTP::Post.new(url, {'Content-Type' => 'application/json'})"
|
||||||
except (json.JSONDecodeError, TypeError):
|
except (json.JSONDecodeError, TypeError):
|
||||||
lines.append("data = " + repr(parsed.data))
|
lines.append("data = " + repr(parsed.data))
|
||||||
request_str = f"Net::HTTP::Post.new(url)"
|
request_str = "Net::HTTP::Post.new(url)"
|
||||||
else:
|
else:
|
||||||
lines.append("data = " + repr(parsed.data))
|
lines.append("data = " + repr(parsed.data))
|
||||||
request_str = f"Net::HTTP::Post.new(url)"
|
request_str = "Net::HTTP::Post.new(url)"
|
||||||
|
|
||||||
if "Content-Type" not in headers and "content-type" not in headers:
|
if "Content-Type" not in headers and "content-type" not in headers:
|
||||||
headers["Content-Type"] = content_type
|
headers["Content-Type"] = content_type
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
"""Parser module for curl commands."""
|
{"""Parser module for curl commands."""
|
||||||
|
|
||||||
import re
|
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
@@ -8,7 +7,7 @@ from typing import Optional
|
|||||||
@dataclass
|
@dataclass
|
||||||
class ParsedCurl:
|
class ParsedCurl:
|
||||||
"""Represents a parsed curl command."""
|
"""Represents a parsed curl command."""
|
||||||
url: str
|
url: str = ""
|
||||||
method: str = "GET"
|
method: str = "GET"
|
||||||
headers: dict = field(default_factory=dict)
|
headers: dict = field(default_factory=dict)
|
||||||
data: Optional[str] = None
|
data: Optional[str] = None
|
||||||
@@ -17,183 +16,122 @@ class ParsedCurl:
|
|||||||
user_agent: Optional[str] = None
|
user_agent: Optional[str] = None
|
||||||
|
|
||||||
|
|
||||||
def unquote(s: str) -> str:
|
|
||||||
"""Remove outer quotes from a string."""
|
|
||||||
if not s:
|
|
||||||
return s
|
|
||||||
if (s.startswith('"') and s.endswith('"')) or (s.startswith("'") and s.endswith("'")):
|
|
||||||
return s[1:-1]
|
|
||||||
return s
|
|
||||||
|
|
||||||
|
|
||||||
def parse_curl(curl_command: str) -> ParsedCurl:
|
|
||||||
"""Parse a curl command string into structured data.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
curl_command: The curl command string to parse.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
ParsedCurl object with extracted components.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
ValueError: If the curl command is invalid.
|
|
||||||
"""
|
|
||||||
if not curl_command.strip():
|
|
||||||
raise ValueError("Empty curl command")
|
|
||||||
|
|
||||||
curl_command = curl_command.strip()
|
|
||||||
if curl_command.startswith("curl "):
|
|
||||||
curl_command = curl_command[5:]
|
|
||||||
elif curl_command.startswith("curl"):
|
|
||||||
curl_command = curl_command[4:]
|
|
||||||
|
|
||||||
tokens = tokenize_command(curl_command)
|
|
||||||
|
|
||||||
url = ""
|
|
||||||
method = "GET"
|
|
||||||
headers = {}
|
|
||||||
data = None
|
|
||||||
auth = None
|
|
||||||
cookies = None
|
|
||||||
user_agent = None
|
|
||||||
|
|
||||||
i = 0
|
|
||||||
while i < len(tokens):
|
|
||||||
token = tokens[i]
|
|
||||||
|
|
||||||
if token == "-X" or token == "--request":
|
|
||||||
if i + 1 < len(tokens):
|
|
||||||
method = tokens[i + 1].upper()
|
|
||||||
i += 2
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif token == "-H" or token == "--header":
|
|
||||||
if i + 1 < len(tokens):
|
|
||||||
header = tokens[i + 1]
|
|
||||||
if ":" in header:
|
|
||||||
key, value = header.split(":", 1)
|
|
||||||
headers[key.strip()] = value.strip()
|
|
||||||
i += 2
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif token == "-d" or token == "--data" or token == "--data-raw":
|
|
||||||
if i + 1 < len(tokens):
|
|
||||||
data = tokens[i + 1]
|
|
||||||
if method == "GET":
|
|
||||||
method = "POST"
|
|
||||||
i += 2
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif token == "-u" or token == "--user":
|
|
||||||
if i + 1 < len(tokens):
|
|
||||||
auth_str = tokens[i + 1]
|
|
||||||
if ":" in auth_str:
|
|
||||||
auth = tuple(auth_str.split(":", 1))
|
|
||||||
else:
|
|
||||||
auth = auth_str
|
|
||||||
i += 2
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif token == "-b" or token == "--cookie":
|
|
||||||
if i + 1 < len(tokens):
|
|
||||||
cookies = tokens[i + 1]
|
|
||||||
i += 2
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif token == "-A" or token == "--user-agent":
|
|
||||||
if i + 1 < len(tokens):
|
|
||||||
user_agent = tokens[i + 1]
|
|
||||||
i += 2
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif token == "-L" or token == "--location" or token == "-s" or token == "--silent" or token == "-S" or token == "--show-error":
|
|
||||||
i += 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
elif token.startswith("-"):
|
|
||||||
i += 1
|
|
||||||
continue
|
|
||||||
|
|
||||||
else:
|
|
||||||
if not url:
|
|
||||||
url = token
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
if not url:
|
|
||||||
raise ValueError("No URL found in curl command")
|
|
||||||
|
|
||||||
if not url.startswith(("http://", "https://")):
|
|
||||||
url = "https://" + url
|
|
||||||
|
|
||||||
if "Authorization" in headers:
|
|
||||||
auth_header = headers["Authorization"]
|
|
||||||
if auth_header.startswith("Basic "):
|
|
||||||
import base64
|
|
||||||
try:
|
|
||||||
encoded = auth_header[6:]
|
|
||||||
decoded = base64.b64decode(encoded).decode("utf-8")
|
|
||||||
if ":" in decoded:
|
|
||||||
auth = tuple(decoded.split(":", 1))
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
elif auth_header.startswith("Bearer "):
|
|
||||||
headers["Authorization"] = auth_header
|
|
||||||
|
|
||||||
return ParsedCurl(
|
|
||||||
url=url,
|
|
||||||
method=method,
|
|
||||||
headers=headers,
|
|
||||||
data=data,
|
|
||||||
auth=auth,
|
|
||||||
cookies=cookies,
|
|
||||||
user_agent=user_agent
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def tokenize_command(cmd: str) -> list:
|
def tokenize_command(cmd: str) -> list:
|
||||||
"""Tokenize a curl command into components, handling quotes and escapes."""
|
"""Tokenize a curl command into arguments."""
|
||||||
tokens = []
|
tokens = []
|
||||||
current = ""
|
current = ""
|
||||||
in_single_quote = False
|
in_single_quote = False
|
||||||
in_double_quote = False
|
in_double_quote = False
|
||||||
escape_next = False
|
escape_next = False
|
||||||
|
|
||||||
i = 0
|
for char in cmd:
|
||||||
while i < len(cmd):
|
|
||||||
char = cmd[i]
|
|
||||||
|
|
||||||
if escape_next:
|
if escape_next:
|
||||||
current += char
|
current += char
|
||||||
escape_next = False
|
escape_next = False
|
||||||
i += 1
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if char == "\\" and not in_single_quote:
|
if char == '\\' and not in_single_quote:
|
||||||
escape_next = True
|
escape_next = True
|
||||||
i += 1
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if char == "'" and not in_double_quote:
|
if char == "'" and not in_double_quote:
|
||||||
in_single_quote = not in_single_quote
|
in_single_quote = not in_single_quote
|
||||||
i += 1
|
current += char
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if char == '"' and not in_single_quote:
|
if char == '"' and not in_single_quote:
|
||||||
in_double_quote = not in_double_quote
|
in_double_quote = not in_double_quote
|
||||||
i += 1
|
current += char
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if char == " " and not in_single_quote and not in_double_quote:
|
if char == ' ' and not in_single_quote and not in_double_quote:
|
||||||
if current:
|
if current:
|
||||||
tokens.append(current)
|
tokens.append(current)
|
||||||
current = ""
|
current = ""
|
||||||
i += 1
|
|
||||||
continue
|
continue
|
||||||
|
|
||||||
current += char
|
current += char
|
||||||
i += 1
|
|
||||||
|
|
||||||
if current:
|
if current:
|
||||||
tokens.append(current)
|
tokens.append(current)
|
||||||
|
|
||||||
return tokens
|
return tokens
|
||||||
|
|
||||||
|
|
||||||
|
def parse_curl(command: str) -> ParsedCurl:
|
||||||
|
"""Parse a curl command string into a ParsedCurl object."""
|
||||||
|
if not command:
|
||||||
|
raise ValueError("Empty curl command")
|
||||||
|
|
||||||
|
command = command.strip()
|
||||||
|
if command.startswith("curl "):
|
||||||
|
command = command[5:]
|
||||||
|
|
||||||
|
tokens = tokenize_command(command)
|
||||||
|
|
||||||
|
if not tokens:
|
||||||
|
raise ValueError("No URL found in curl command")
|
||||||
|
|
||||||
|
parsed = ParsedCurl()
|
||||||
|
|
||||||
|
i = 0
|
||||||
|
while i < len(tokens):
|
||||||
|
token = tokens[i]
|
||||||
|
|
||||||
|
if not token.startswith("-"):
|
||||||
|
if not parsed.url:
|
||||||
|
parsed.url = token
|
||||||
|
i += 1
|
||||||
|
continue
|
||||||
|
|
||||||
|
if token in ("-X", "--request"):
|
||||||
|
if i + 1 < len(tokens):
|
||||||
|
parsed.method = tokens[i + 1].upper()
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
|
||||||
|
if token in ("-H", "--header"):
|
||||||
|
if i + 1 < len(tokens):
|
||||||
|
header = tokens[i + 1]
|
||||||
|
if ":" in header:
|
||||||
|
key, value = header.split(":", 1)
|
||||||
|
parsed.headers[key.strip()] = value.strip()
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
|
||||||
|
if token in ("-d", "--data", "--data-raw", "--data-binary"):
|
||||||
|
if i + 1 < len(tokens):
|
||||||
|
parsed.data = tokens[i + 1]
|
||||||
|
if parsed.method == "GET":
|
||||||
|
parsed.method = "POST"
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
|
||||||
|
if token in ("-u", "--user"):
|
||||||
|
if i + 1 < len(tokens):
|
||||||
|
auth = tokens[i + 1]
|
||||||
|
if ":" in auth:
|
||||||
|
parsed.auth = auth.split(":", 1)
|
||||||
|
else:
|
||||||
|
parsed.auth = (auth, "")
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
|
||||||
|
if token in ("-b", "--cookie"):
|
||||||
|
if i + 1 < len(tokens):
|
||||||
|
parsed.cookies = tokens[i + 1]
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
|
||||||
|
if token in ("-A", "--user-agent"):
|
||||||
|
if i + 1 < len(tokens):
|
||||||
|
parsed.user_agent = tokens[i + 1]
|
||||||
|
i += 2
|
||||||
|
continue
|
||||||
|
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if not parsed.url:
|
||||||
|
raise ValueError("No URL found in curl command")
|
||||||
|
|
||||||
|
return parsed
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
"""Tests for code generators."""
|
{"""Tests for code generators."""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from curlconverter.parser import parse_curl
|
from curlconverter.parser import parse_curl
|
||||||
@@ -9,60 +9,48 @@ class TestPythonGenerator:
|
|||||||
"""Tests for Python code generator."""
|
"""Tests for Python code generator."""
|
||||||
|
|
||||||
def test_basic_get_request(self):
|
def test_basic_get_request(self):
|
||||||
"""Test generating Python code for GET request."""
|
"""Test basic GET request."""
|
||||||
curl = "curl https://api.example.com/users"
|
parsed = parse_curl("curl http://example.com")
|
||||||
parsed = parse_curl(curl)
|
|
||||||
code = generate_code(parsed, "python")
|
code = generate_code(parsed, "python")
|
||||||
|
|
||||||
assert "import requests" in code
|
assert "import requests" in code
|
||||||
assert "https://api.example.com/users" in code
|
assert "url = 'http://example.com'" in code
|
||||||
assert "requests.get" in code
|
assert "requests.get" in code
|
||||||
|
|
||||||
def test_post_with_json(self):
|
def test_post_with_json(self):
|
||||||
"""Test generating Python code for POST with JSON."""
|
"""Test POST with JSON data."""
|
||||||
curl = 'curl -X POST -H "Content-Type: application/json" -d \'{"name":"test"}\' https://api.example.com/users'
|
parsed = parse_curl('curl -X POST -H "Content-Type: application/json" -d \'{"key":"value"}\' http://example.com')
|
||||||
parsed = parse_curl(curl)
|
|
||||||
code = generate_code(parsed, "python")
|
code = generate_code(parsed, "python")
|
||||||
|
|
||||||
assert "requests.post" in code
|
assert "requests.post" in code
|
||||||
assert "json=data" in code or "json=data" in code
|
assert "json=data" in code
|
||||||
|
|
||||||
def test_basic_auth(self):
|
def test_basic_auth(self):
|
||||||
"""Test generating Python code with basic auth."""
|
"""Test basic authentication."""
|
||||||
curl = "curl -u user:pass https://api.example.com"
|
parsed = parse_curl("curl -u user:pass http://example.com")
|
||||||
parsed = parse_curl(curl)
|
|
||||||
code = generate_code(parsed, "python")
|
code = generate_code(parsed, "python")
|
||||||
|
assert "Authorization" in code
|
||||||
assert "Authorization" in code or "requests" in code
|
assert "Basic" in code
|
||||||
|
|
||||||
def test_headers(self):
|
def test_headers(self):
|
||||||
"""Test generating Python code with headers."""
|
"""Test custom headers."""
|
||||||
curl = 'curl -H "Authorization: Bearer token" https://api.example.com'
|
parsed = parse_curl('curl -H "X-Custom: header" http://example.com')
|
||||||
parsed = parse_curl(curl)
|
|
||||||
code = generate_code(parsed, "python")
|
code = generate_code(parsed, "python")
|
||||||
|
assert "X-Custom" in code
|
||||||
assert "headers" in code
|
|
||||||
|
|
||||||
|
|
||||||
class TestJavaScriptGenerator:
|
class TestJavaScriptGenerator:
|
||||||
"""Tests for JavaScript code generator."""
|
"""Tests for JavaScript code generator."""
|
||||||
|
|
||||||
def test_basic_get_request(self):
|
def test_basic_get_request(self):
|
||||||
"""Test generating JavaScript code for GET request."""
|
"""Test basic GET request."""
|
||||||
curl = "curl https://api.example.com/users"
|
parsed = parse_curl("curl http://example.com")
|
||||||
parsed = parse_curl(curl)
|
|
||||||
code = generate_code(parsed, "javascript")
|
code = generate_code(parsed, "javascript")
|
||||||
|
|
||||||
assert "fetch" in code
|
assert "fetch" in code
|
||||||
assert "https://api.example.com/users" in code
|
assert "url = 'http://example.com'" in code
|
||||||
assert "GET" in code
|
|
||||||
|
|
||||||
def test_post_with_data(self):
|
def test_post_with_data(self):
|
||||||
"""Test generating JavaScript code for POST."""
|
"""Test POST with data."""
|
||||||
curl = "curl -X POST -d 'name=test' https://api.example.com/users"
|
parsed = parse_curl("curl -X POST -d 'key=value' http://example.com")
|
||||||
parsed = parse_curl(curl)
|
|
||||||
code = generate_code(parsed, "javascript")
|
code = generate_code(parsed, "javascript")
|
||||||
|
|
||||||
assert "POST" in code
|
assert "POST" in code
|
||||||
assert "body" in code
|
assert "body" in code
|
||||||
|
|
||||||
@@ -71,76 +59,61 @@ class TestGoGenerator:
|
|||||||
"""Tests for Go code generator."""
|
"""Tests for Go code generator."""
|
||||||
|
|
||||||
def test_basic_get_request(self):
|
def test_basic_get_request(self):
|
||||||
"""Test generating Go code for GET request."""
|
"""Test basic GET request."""
|
||||||
curl = "curl https://api.example.com/users"
|
parsed = parse_curl("curl http://example.com")
|
||||||
parsed = parse_curl(curl)
|
|
||||||
code = generate_code(parsed, "go")
|
code = generate_code(parsed, "go")
|
||||||
|
|
||||||
assert "package main" in code
|
assert "package main" in code
|
||||||
assert "net/http" in code
|
assert "net/http" in code
|
||||||
assert "https://api.example.com/users" in code
|
assert "http.NewRequest" in code
|
||||||
|
|
||||||
def test_post_request(self):
|
def test_post_request(self):
|
||||||
"""Test generating Go code for POST."""
|
"""Test POST request."""
|
||||||
curl = "curl -X POST -d 'name=test' https://api.example.com/users"
|
parsed = parse_curl("curl -X POST -d 'key=value' http://example.com")
|
||||||
parsed = parse_curl(curl)
|
|
||||||
code = generate_code(parsed, "go")
|
code = generate_code(parsed, "go")
|
||||||
|
|
||||||
assert "POST" in code
|
assert "POST" in code
|
||||||
assert "bytes.NewBuffer" in code
|
assert "strings.NewReader" in code
|
||||||
|
|
||||||
|
|
||||||
class TestRubyGenerator:
|
class TestRubyGenerator:
|
||||||
"""Tests for Ruby code generator."""
|
"""Tests for Ruby code generator."""
|
||||||
|
|
||||||
def test_basic_get_request(self):
|
def test_basic_get_request(self):
|
||||||
"""Test generating Ruby code for GET request."""
|
"""Test basic GET request."""
|
||||||
curl = "curl https://api.example.com/users"
|
parsed = parse_curl("curl http://example.com")
|
||||||
parsed = parse_curl(curl)
|
|
||||||
code = generate_code(parsed, "ruby")
|
code = generate_code(parsed, "ruby")
|
||||||
|
assert "net/http" in code
|
||||||
assert "require 'net/http'" in code
|
assert "Net::HTTP" in code
|
||||||
assert "Net::HTTP::Get" in code
|
|
||||||
assert "https://api.example.com/users" in code
|
|
||||||
|
|
||||||
def test_post_request(self):
|
def test_post_request(self):
|
||||||
"""Test generating Ruby code for POST."""
|
"""Test POST request."""
|
||||||
curl = "curl -X POST -d 'name=test' https://api.example.com/users"
|
parsed = parse_curl("curl -X POST -d 'key=value' http://example.com")
|
||||||
parsed = parse_curl(curl)
|
|
||||||
code = generate_code(parsed, "ruby")
|
code = generate_code(parsed, "ruby")
|
||||||
|
assert "Post" in code
|
||||||
assert "Net::HTTP::Post" in code
|
|
||||||
|
|
||||||
|
|
||||||
class TestPHPGenerator:
|
class TestPHPGenerator:
|
||||||
"""Tests for PHP code generator."""
|
"""Tests for PHP code generator."""
|
||||||
|
|
||||||
def test_basic_get_request(self):
|
def test_basic_get_request(self):
|
||||||
"""Test generating PHP code for GET request."""
|
"""Test basic GET request."""
|
||||||
curl = "curl https://api.example.com/users"
|
parsed = parse_curl("curl http://example.com")
|
||||||
parsed = parse_curl(curl)
|
|
||||||
code = generate_code(parsed, "php")
|
code = generate_code(parsed, "php")
|
||||||
|
|
||||||
assert "<?php" in code
|
|
||||||
assert "curl_init" in code
|
assert "curl_init" in code
|
||||||
assert "https://api.example.com/users" in code
|
assert "$url" in code
|
||||||
|
|
||||||
def test_post_request(self):
|
def test_post_request(self):
|
||||||
"""Test generating PHP code for POST."""
|
"""Test POST request."""
|
||||||
curl = "curl -X POST -d 'name=test' https://api.example.com/users"
|
parsed = parse_curl("curl -X POST -d 'key=value' http://example.com")
|
||||||
parsed = parse_curl(curl)
|
|
||||||
code = generate_code(parsed, "php")
|
code = generate_code(parsed, "php")
|
||||||
|
assert "CURLOPT_POST" in code or "CURLOPT_CUSTOMREQUEST" in code
|
||||||
assert "CURLOPT_POST" in code or "CUSTOMREQUEST" in code
|
|
||||||
|
|
||||||
|
|
||||||
class TestSupportedLanguages:
|
class TestSupportedLanguages:
|
||||||
"""Tests for supported languages."""
|
"""Tests for supported languages."""
|
||||||
|
|
||||||
def test_get_supported_languages(self):
|
def test_get_supported_languages(self):
|
||||||
"""Test getting list of supported languages."""
|
"""Test getting supported languages."""
|
||||||
languages = get_supported_languages()
|
languages = get_supported_languages()
|
||||||
|
|
||||||
assert "python" in languages
|
assert "python" in languages
|
||||||
assert "javascript" in languages
|
assert "javascript" in languages
|
||||||
assert "go" in languages
|
assert "go" in languages
|
||||||
@@ -148,11 +121,7 @@ class TestSupportedLanguages:
|
|||||||
assert "php" in languages
|
assert "php" in languages
|
||||||
|
|
||||||
def test_unsupported_language_raises_error(self):
|
def test_unsupported_language_raises_error(self):
|
||||||
"""Test that unsupported language raises ValueError."""
|
"""Test unsupported language raises error."""
|
||||||
curl = "curl https://example.com"
|
parsed = parse_curl("curl http://example.com")
|
||||||
parsed = parse_curl(curl)
|
with pytest.raises(ValueError, match="Unsupported language"):
|
||||||
|
generate_code(parsed, "unsupported")
|
||||||
with pytest.raises(ValueError) as exc_info:
|
|
||||||
generate_code(parsed, "unsupported_lang")
|
|
||||||
|
|
||||||
assert "Unsupported language" in str(exc_info.value)
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
"""Tests for the curl parser module."""
|
{"""Tests for the parser module."""
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
from curlconverter.parser import parse_curl, ParsedCurl, tokenize_command
|
from curlconverter.parser import parse_curl, tokenize_command, ParsedCurl
|
||||||
|
|
||||||
|
|
||||||
class TestTokenizeCommand:
|
class TestTokenizeCommand:
|
||||||
@@ -9,120 +9,101 @@ class TestTokenizeCommand:
|
|||||||
|
|
||||||
def test_simple_tokens(self):
|
def test_simple_tokens(self):
|
||||||
"""Test simple command tokenization."""
|
"""Test simple command tokenization."""
|
||||||
tokens = tokenize_command("curl -X POST https://example.com")
|
tokens = tokenize_command("curl -X GET http://example.com")
|
||||||
assert "curl" in tokens
|
assert tokens == ["curl", "-X", "GET", "http://example.com"]
|
||||||
assert "-X" in tokens
|
|
||||||
assert "POST" in tokens
|
|
||||||
assert "https://example.com" in tokens
|
|
||||||
|
|
||||||
def test_quoted_strings(self):
|
def test_quoted_strings(self):
|
||||||
"""Test handling of quoted strings."""
|
"""Test quoted string tokenization."""
|
||||||
tokens = tokenize_command('-H "Content-Type: application/json"')
|
tokens = tokenize_command('curl -H "Content-Type: application/json" http://example.com')
|
||||||
assert '-H' in tokens
|
assert "-H" in tokens
|
||||||
assert 'Content-Type: application/json' in tokens
|
assert 'Content-Type: application/json' in tokens
|
||||||
|
|
||||||
def test_single_quotes(self):
|
def test_single_quotes(self):
|
||||||
"""Test handling of single quotes."""
|
"""Test single quote tokenization."""
|
||||||
tokens = tokenize_command("-d '{\"key\": \"value\"}'")
|
tokens = tokenize_command("curl -d 'hello world' http://example.com")
|
||||||
assert "-d" in tokens
|
assert "-d" in tokens
|
||||||
assert '{"key": "value"}' in tokens
|
assert "'hello world'" in tokens
|
||||||
|
|
||||||
|
|
||||||
class TestParseCurl:
|
class TestParseCurl:
|
||||||
"""Tests for parse_curl function."""
|
"""Tests for parse_curl function."""
|
||||||
|
|
||||||
def test_basic_get_request(self):
|
def test_basic_get_request(self):
|
||||||
"""Test parsing a basic GET request."""
|
"""Test basic GET request parsing."""
|
||||||
curl = "curl https://example.com"
|
parsed = parse_curl("curl http://example.com")
|
||||||
result = parse_curl(curl)
|
assert parsed.url == "http://example.com"
|
||||||
assert result.url == "https://example.com"
|
assert parsed.method == "GET"
|
||||||
assert result.method == "GET"
|
|
||||||
|
|
||||||
def test_url_with_protocol(self):
|
def test_url_with_protocol(self):
|
||||||
"""Test URL with http protocol."""
|
"""Test URL with protocol."""
|
||||||
curl = "curl http://example.com"
|
parsed = parse_curl("curl https://api.example.com/endpoint")
|
||||||
result = parse_curl(curl)
|
assert parsed.url == "https://api.example.com/endpoint"
|
||||||
assert result.url == "http://example.com"
|
|
||||||
|
|
||||||
def test_post_request(self):
|
def test_post_request(self):
|
||||||
"""Test parsing POST request with -X."""
|
"""Test POST request parsing."""
|
||||||
curl = "curl -X POST https://api.example.com/users"
|
parsed = parse_curl("curl -X POST http://example.com")
|
||||||
result = parse_curl(curl)
|
assert parsed.method == "POST"
|
||||||
assert result.url == "https://api.example.com/users"
|
|
||||||
assert result.method == "POST"
|
|
||||||
|
|
||||||
def test_post_with_data(self):
|
def test_post_with_data(self):
|
||||||
"""Test parsing POST with -d flag."""
|
"""Test POST with data."""
|
||||||
curl = "curl -d 'name=test' https://api.example.com/users"
|
parsed = parse_curl("curl -d 'key=value' http://example.com")
|
||||||
result = parse_curl(curl)
|
assert parsed.data == "key=value"
|
||||||
assert result.method == "POST"
|
assert parsed.method == "POST"
|
||||||
assert result.data == "name=test"
|
|
||||||
|
|
||||||
def test_post_with_data_raw(self):
|
def test_post_with_data_raw(self):
|
||||||
"""Test parsing POST with --data-raw flag."""
|
"""Test POST with --data-raw."""
|
||||||
curl = 'curl --data-raw {"name":"test"} https://api.example.com/users'
|
parsed = parse_curl("curl --data-raw '{"key": "value"}' http://example.com")
|
||||||
result = parse_curl(curl)
|
assert parsed.data == '{"key": "value"}'
|
||||||
assert result.method == "POST"
|
assert parsed.method == "POST"
|
||||||
assert result.data == '{name:test}'
|
|
||||||
|
|
||||||
def test_headers(self):
|
def test_headers(self):
|
||||||
"""Test parsing headers."""
|
"""Test header parsing."""
|
||||||
curl = 'curl -H "Content-Type: application/json" -H "Authorization: Bearer token" https://api.example.com'
|
parsed = parse_curl('curl -H "Content-Type: application/json" http://example.com')
|
||||||
result = parse_curl(curl)
|
assert "Content-Type" in parsed.headers
|
||||||
assert "Content-Type" in result.headers
|
assert parsed.headers["Content-Type"] == "application/json"
|
||||||
assert result.headers["Content-Type"] == "application/json"
|
|
||||||
assert "Authorization" in result.headers
|
|
||||||
|
|
||||||
def test_basic_auth(self):
|
def test_basic_auth(self):
|
||||||
"""Test parsing basic authentication."""
|
"""Test basic auth parsing."""
|
||||||
curl = "curl -u user:pass https://api.example.com"
|
parsed = parse_curl("curl -u user:pass http://example.com")
|
||||||
result = parse_curl(curl)
|
assert parsed.auth == ("user", "pass")
|
||||||
assert result.auth == ("user", "pass")
|
|
||||||
|
|
||||||
def test_cookies(self):
|
def test_cookies(self):
|
||||||
"""Test parsing cookies."""
|
"""Test cookie parsing."""
|
||||||
curl = "curl -b 'session=abc123' https://example.com"
|
parsed = parse_curl("curl -b 'session=abc123' http://example.com")
|
||||||
result = parse_curl(curl)
|
assert parsed.cookies == "session=abc123"
|
||||||
assert result.cookies == "session=abc123"
|
|
||||||
|
|
||||||
def test_user_agent(self):
|
def test_user_agent(self):
|
||||||
"""Test parsing user agent."""
|
"""Test user-agent parsing."""
|
||||||
curl = "curl -A 'Mozilla/5.0' https://example.com"
|
parsed = parse_curl("curl -A 'Mozilla/5.0' http://example.com")
|
||||||
result = parse_curl(curl)
|
assert parsed.user_agent == "Mozilla/5.0"
|
||||||
assert result.user_agent == "Mozilla/5.0"
|
|
||||||
|
|
||||||
def test_full_command(self):
|
def test_full_command(self):
|
||||||
"""Test parsing a complete curl command."""
|
"""Test full curl command with all options."""
|
||||||
curl = '''curl -X POST \
|
cmd = 'curl -X POST -H "Content-Type: application/json" -d \'{"key":"value"}\' -u user:pass -b "session=abc" -A "Mozilla" http://example.com/api'
|
||||||
-H "Content-Type: application/json" \
|
parsed = parse_curl(cmd)
|
||||||
-H "Authorization: Bearer token" \
|
|
||||||
-d '{\"name\":\"test\"}' \
|
assert parsed.url == "http://example.com/api"
|
||||||
-u user:pass \
|
assert parsed.method == "POST"
|
||||||
-b "session=abc" \
|
assert "Content-Type" in parsed.headers
|
||||||
https://api.example.com/users'''
|
assert parsed.data == '{"key":"value"}'
|
||||||
result = parse_curl(curl)
|
assert parsed.auth == ("user", "pass")
|
||||||
assert result.url == "https://api.example.com/users"
|
assert parsed.cookies == "session=abc"
|
||||||
assert result.method == "POST"
|
assert parsed.user_agent == "Mozilla"
|
||||||
assert "Content-Type" in result.headers
|
|
||||||
assert result.data == '{"name":"test"}'
|
|
||||||
assert result.auth == ("user", "pass")
|
|
||||||
assert result.cookies == "session=abc"
|
|
||||||
|
|
||||||
def test_empty_command_raises_error(self):
|
def test_empty_command_raises_error(self):
|
||||||
"""Test that empty command raises ValueError."""
|
"""Test empty command raises error."""
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError, match="Empty curl command"):
|
||||||
parse_curl("")
|
parse_curl("")
|
||||||
|
|
||||||
def test_no_url_raises_error(self):
|
def test_no_url_raises_error(self):
|
||||||
"""Test that command without URL raises ValueError."""
|
"""Test command without URL raises error."""
|
||||||
with pytest.raises(ValueError):
|
with pytest.raises(ValueError, match="No URL found"):
|
||||||
parse_curl("curl -X POST")
|
parse_curl("curl -X POST")
|
||||||
|
|
||||||
def test_curl_prefix_optional(self):
|
def test_curl_prefix_optional(self):
|
||||||
"""Test that 'curl' prefix is optional."""
|
"""Test that 'curl' prefix is optional."""
|
||||||
result = parse_curl("https://example.com")
|
parsed = parse_curl("http://example.com")
|
||||||
assert result.url == "https://example.com"
|
assert parsed.url == "http://example.com"
|
||||||
assert result.method == "GET"
|
|
||||||
|
|
||||||
|
|
||||||
class TestParsedCurl:
|
class TestParsedCurl:
|
||||||
@@ -130,11 +111,11 @@ class TestParsedCurl:
|
|||||||
|
|
||||||
def test_default_values(self):
|
def test_default_values(self):
|
||||||
"""Test default values."""
|
"""Test default values."""
|
||||||
curl = ParsedCurl(url="https://example.com")
|
parsed = ParsedCurl(url="http://example.com")
|
||||||
assert curl.url == "https://example.com"
|
assert parsed.url == "http://example.com"
|
||||||
assert curl.method == "GET"
|
assert parsed.method == "GET"
|
||||||
assert curl.headers == {}
|
assert parsed.headers == {}
|
||||||
assert curl.data is None
|
assert parsed.data is None
|
||||||
assert curl.auth is None
|
assert parsed.auth is None
|
||||||
assert curl.cookies is None
|
assert parsed.cookies is None
|
||||||
assert curl.user_agent is None
|
assert parsed.user_agent is None
|
||||||
Reference in New Issue
Block a user