feat: add HTTPie export format in copy-as

- New toHTTPie() function builds an httpie command from raw request
- Added "httpie" case in formatAs() switch
- Uses util.ParseRawRequest; model lists httpie as a selectable format

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Hadi
2026-05-19 13:38:50 +02:00
parent b490c7a0ac
commit 6a9935ec27
2 changed files with 32 additions and 34 deletions
+31 -34
View File
@@ -5,6 +5,8 @@ import (
"fmt" "fmt"
"net/url" "net/url"
"strings" "strings"
"github.com/anotherhadi/spilltea/internal/util"
) )
type header struct{ key, value string } type header struct{ key, value string }
@@ -14,46 +16,22 @@ type parsedRequest struct {
path string path string
host string host string
scheme string scheme string
headers []header headers []header // garder header{key, value} pour compat locale
body string body string
} }
func parseRaw(raw, scheme string) parsedRequest { func parseRaw(raw, scheme string) parsedRequest {
lines := strings.Split(strings.ReplaceAll(raw, "\r\n", "\n"), "\n") r := util.ParseRawRequest(raw)
pr := parsedRequest{scheme: scheme} pr := parsedRequest{
if len(lines) == 0 { method: r.Method,
return pr path: r.Path,
host: r.Host,
scheme: scheme,
} }
for _, h := range r.Headers {
parts := strings.SplitN(lines[0], " ", 3) pr.headers = append(pr.headers, header{h.Key, h.Value})
if len(parts) >= 1 {
pr.method = strings.TrimSpace(parts[0])
}
if len(parts) >= 2 {
pr.path = strings.TrimSpace(parts[1])
}
i := 1
for i < len(lines) {
line := strings.TrimRight(lines[i], "\r")
if line == "" {
i++
break
}
if kv := strings.SplitN(line, ": ", 2); len(kv) == 2 {
k := strings.TrimSpace(kv[0])
v := strings.TrimSpace(kv[1])
pr.headers = append(pr.headers, header{k, v})
if strings.EqualFold(k, "host") {
pr.host = v
}
}
i++
}
if i < len(lines) {
pr.body = strings.TrimRight(strings.Join(lines[i:], "\n"), "\n")
} }
pr.body = r.Body
return pr return pr
} }
@@ -82,10 +60,29 @@ func formatAs(id, raw, scheme string) string {
return toMarkdown(pr) return toMarkdown(pr)
case "har": case "har":
return toHAR(pr) return toHAR(pr)
case "httpie":
return toHTTPie(pr)
} }
return raw return raw
} }
func toHTTPie(pr parsedRequest) string {
var sb strings.Builder
method := strings.ToUpper(pr.method)
fmt.Fprintf(&sb, "http %s '%s'", method, pr.fullURL())
for _, h := range pr.headers {
if strings.EqualFold(h.key, "content-length") {
continue
}
fmt.Fprintf(&sb, " \\\n '%s:%s'", h.key, h.value)
}
if pr.body != "" {
// Pass body via stdin hint
fmt.Fprintf(&sb, " \\\n <<< %q", pr.body)
}
return sb.String()
}
func toMarkdown(pr parsedRequest) string { func toMarkdown(pr parsedRequest) string {
var sb strings.Builder var sb strings.Builder
fmt.Fprintf(&sb, "### %s %s\n\n", pr.method, pr.fullURL()) fmt.Fprintf(&sb, "### %s %s\n\n", pr.method, pr.fullURL())
+1
View File
@@ -46,6 +46,7 @@ var allFormats = []list.Item{
formatItem{"ffuf", "FFUF", "web fuzzer: FUZZ in query string"}, formatItem{"ffuf", "FFUF", "web fuzzer: FUZZ in query string"},
formatItem{"markdown", "Markdown", "formatted for documentation"}, formatItem{"markdown", "Markdown", "formatted for documentation"},
formatItem{"har", "HAR", "HTTP Archive (JSON)"}, formatItem{"har", "HAR", "HTTP Archive (JSON)"},
formatItem{"httpie", "HTTPie", "HTTPie command line client"},
} }
type Model struct { type Model struct {