func newTokenBuffer(dec *json.Decoder) (*tokenBuffer, error) {
	dec.UseNumber()
	tks := make([]json.Token, 0, 64)
	for {
		tok, err := dec.Token()
		switch err {
		case nil:
			tks = append(tks, tok)
		case io.EOF:
			return &tokenBuffer{tokens: tks}, nil
		default:
			return nil, err
		}
	}
}
newTokenBuffer allows you to create a tokenBuffer which contains all the
tokens of the given json.Decoder.
package output
import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"sort"
	"strings"
	"code.cloudfoundry.org/bytefmt"
	"github.com/logrusorgru/aurora"
	"github.com/pkg/errors"
)
type PrettyPrinter struct {
	writer        io.Writer
	plain         Printer
	aurora        aurora.Aurora
	headerPalette *HeaderPalette
	jsonPalette   *JSONPalette
	indentWidth   int
}
type PrettyPrinterConfig struct {
	Writer      io.Writer
	EnableColor bool
}
type HeaderPalette struct {
	Method              aurora.Color
	URL                 aurora.Color
	Proto               aurora.Color
	SuccessfulStatus    aurora.Color
	NonSuccessfulStatus aurora.Color
	FieldName           aurora.Color
	FieldValue          aurora.Color
	FieldSeparator      aurora.Color
}
var defaultHeaderPalette = HeaderPalette{
	Method:              aurora.WhiteFg | aurora.BoldFm,
	URL:                 aurora.GreenFg | aurora.BoldFm,
	Proto:               aurora.BlueFg,
	SuccessfulStatus:    aurora.GreenFg | aurora.BoldFm,
	NonSuccessfulStatus: aurora.YellowFg | aurora.BoldFm,
	FieldName:           aurora.WhiteFg,
	FieldValue:          aurora.CyanFg,
	FieldSeparator:      aurora.WhiteFg,
}
type JSONPalette struct {
	Key       aurora.Color
	String    aurora.Color
	Number    aurora.Color
	Boolean   aurora.Color
	Null      aurora.Color
	Delimiter aurora.Color
}
var defaultJSONPalette = JSONPalette{
	Key:       aurora.BlueFg,
	String:    aurora.YellowFg,
	Number:    aurora.CyanFg,
	Boolean:   aurora.RedFg | aurora.BoldFm,
	Null:      aurora.RedFg | aurora.BoldFm,
	Delimiter: aurora.WhiteFg,
}
var errMalformedJSON = errors.New("output: malformed json")
func NewPrettyPrinter(config PrettyPrinterConfig) Printer {
	return &PrettyPrinter{
		writer:        config.Writer,
		plain:         NewPlainPrinter(config.Writer),
		aurora:        aurora.NewAurora(config.EnableColor),
		headerPalette: &defaultHeaderPalette,
		jsonPalette:   &defaultJSONPalette,
		indentWidth:   4,
	}
}
func (p *PrettyPrinter) PrintStatusLine(proto string, status string, statusCode int) error {
	var statusColor aurora.Color
	if 200 <= statusCode && statusCode < 300 {
		statusColor = p.headerPalette.SuccessfulStatus
	} else {
		statusColor = p.headerPalette.NonSuccessfulStatus
	}
	fmt.Fprintf(p.writer, "%s %s\n",
		p.aurora.Colorize(proto, p.headerPalette.Proto),
		p.aurora.Colorize(status, statusColor),
	)
	return nil
}
func (p *PrettyPrinter) PrintRequestLine(req *http.Request) error {
	fmt.Fprintf(p.writer, "%s %s %s\n",
		p.aurora.Colorize(req.Method, p.headerPalette.Method),
		p.aurora.Colorize(req.URL, p.headerPalette.URL),
		p.aurora.Colorize(req.Proto, p.headerPalette.Proto),
	)
	return nil
}
func (p *PrettyPrinter) PrintHeader(header http.Header) error {
	var names []string
	for name := range header {
		names = append(names, name)
	}
	sort.Strings(names)
	for _, name := range names {
		values := header[name]
		for _, value := range values {
			fmt.Fprintf(p.writer, "%s%s %s\n",
				p.aurora.Colorize(name, p.headerPalette.FieldName),
				p.aurora.Colorize(":", p.headerPalette.FieldSeparator),
				p.aurora.Colorize(value, p.headerPalette.FieldValue))
		}
	}
	fmt.Fprintln(p.writer)
	return nil
}
func isJSON(contentType string) bool {
	contentType = strings.TrimSpace(contentType)
	semicolon := strings.Index(contentType, ";")
	if semicolon != -1 {
		contentType = contentType[:semicolon]
	}
	return contentType == "application/json" || strings.HasSuffix(contentType, "+json")
}
func (p *PrettyPrinter) PrintBody(body io.Reader, contentType string) error {
	if !isJSON(contentType) {
		return p.plain.PrintBody(body, contentType)
	}
	content, err := ioutil.ReadAll(body)
	if err != nil {
		return errors.Wrap(err, "reading body")
	}
	toks, err := newTokenBuffer(json.NewDecoder(bytes.NewReader(content)))
	if err != nil || len(toks.tokens) == 0 {
		p.writer.Write(content)
		return nil
	}
	err = p.printJSON(toks, 0)
	if err != nil && !errors.Is(err, errMalformedJSON) {
		return err
	}
	fmt.Fprintln(p.writer)
	return nil
}