cmd/httptap/cmd/export.go

package cmd

import (
	"context"
	"fmt"
	"os"
	"time"

	"github.com/spf13/cobra"

	"mercemay.top/httptap/internal/export/curl"
	"mercemay.top/httptap/internal/export/har"
	"mercemay.top/httptap/internal/export/markdown"
	"mercemay.top/httptap/internal/export/raw"
	"mercemay.top/httptap/internal/parser"
	"mercemay.top/httptap/internal/source"
)

type exportFlags struct {
	Format string
	Output string
	Input  string
}

func newExportCmd() *cobra.Command {
	var ef exportFlags
	cmd := &cobra.Command{
		Use:   "export",
		Short: "Convert a capture file to another format",
		RunE: func(c *cobra.Command, args []string) error {
			return runExport(ef)
		},
	}
	cmd.Flags().StringVarP(&ef.Format, "format", "f", "har",
		"one of har|curl|raw|markdown")
	cmd.Flags().StringVarP(&ef.Output, "output", "o", "-",
		"destination path, '-' for stdout")
	cmd.Flags().StringVarP(&ef.Input, "input", "i", "",
		"source capture file (HAR or raw)")
	_ = cmd.MarkFlagRequired("input")
	return cmd
}

func runExport(ef exportFlags) error {
	src, err := source.OpenFile(ef.Input)
	if err != nil {
		return err
	}
	defer src.Close()
	out := os.Stdout
	if ef.Output != "-" {
		out, err = os.Create(ef.Output)
		if err != nil {
			return err
		}
		defer out.Close()
	}

	// Collect every message, then render in one pass.
	var msgs []parser.Message
	ctx := context.Background()
	for {
		ev, err := src.Next(ctx)
		if err != nil {
			break
		}
		p := parser.New(bytesReader(ev.Payload), func(m parser.Message) {
			msgs = append(msgs, m)
		})
		_ = p.Run()
	}

	switch ef.Format {
	case "har":
		entries := make([]har.Entry, 0, len(msgs)/2)
		for i := 0; i+1 < len(msgs); i += 2 {
			entries = append(entries, har.FromPair(msgs[i], msgs[i+1], time.Now(), 0))
		}
		return har.Write(out, entries)
	case "curl":
		for _, m := range msgs {
			fmt.Fprintln(out, curl.RenderPretty(m))
		}
	case "raw":
		for _, m := range msgs {
			if err := raw.WriteMessage(out, m); err != nil {
				return err
			}
		}
	case "markdown":
		for i := 0; i+1 < len(msgs); i += 2 {
			_ = markdown.Write(out, msgs[i], msgs[i+1], time.Now(), 0, markdown.Default())
		}
	default:
		return fmt.Errorf("export: unknown format %q", ef.Format)
	}
	return nil
}