Mainflux.mainflux/vendor/github.com/hokaccha/go-prettyjson/prettyjson.go

192 lines
4.8 KiB
Go

// Package prettyjson provides JSON pretty print.
package prettyjson
import (
"bytes"
"encoding/json"
"fmt"
"sort"
"strconv"
"strings"
"github.com/fatih/color"
)
// Formatter is a struct to format JSON data. `color` is github.com/fatih/color: https://github.com/fatih/color
type Formatter struct {
// JSON key color. Default is `color.New(color.FgBlue, color.Bold)`.
KeyColor *color.Color
// JSON string value color. Default is `color.New(color.FgGreen, color.Bold)`.
StringColor *color.Color
// JSON boolean value color. Default is `color.New(color.FgYellow, color.Bold)`.
BoolColor *color.Color
// JSON number value color. Default is `color.New(color.FgCyan, color.Bold)`.
NumberColor *color.Color
// JSON null value color. Default is `color.New(color.FgBlack, color.Bold)`.
NullColor *color.Color
// Max length of JSON string value. When the value is 1 and over, string is truncated to length of the value.
// Default is 0 (not truncated).
StringMaxLength int
// Boolean to disable color. Default is false.
DisabledColor bool
// Indent space number. Default is 2.
Indent int
// Newline string. To print without new lines set it to empty string. Default is \n.
Newline string
}
// NewFormatter returns a new formatter with following default values.
func NewFormatter() *Formatter {
return &Formatter{
KeyColor: color.New(color.FgBlue, color.Bold),
StringColor: color.New(color.FgGreen, color.Bold),
BoolColor: color.New(color.FgYellow, color.Bold),
NumberColor: color.New(color.FgCyan, color.Bold),
NullColor: color.New(color.FgBlack, color.Bold),
StringMaxLength: 0,
DisabledColor: false,
Indent: 2,
Newline: "\n",
}
}
// Marshal marshals and formats JSON data.
func (f *Formatter) Marshal(v interface{}) ([]byte, error) {
data, err := json.Marshal(v)
if err != nil {
return nil, err
}
return f.Format(data)
}
// Format formats JSON string.
func (f *Formatter) Format(data []byte) ([]byte, error) {
var v interface{}
if err := json.Unmarshal(data, &v); err != nil {
return nil, err
}
return []byte(f.pretty(v, 1)), nil
}
func (f *Formatter) sprintfColor(c *color.Color, format string, args ...interface{}) string {
if f.DisabledColor || c == nil {
return fmt.Sprintf(format, args...)
}
return c.SprintfFunc()(format, args...)
}
func (f *Formatter) sprintColor(c *color.Color, s string) string {
if f.DisabledColor || c == nil {
return fmt.Sprint(s)
}
return c.SprintFunc()(s)
}
func (f *Formatter) pretty(v interface{}, depth int) string {
switch val := v.(type) {
case string:
return f.processString(val)
case float64:
return f.sprintColor(f.NumberColor, strconv.FormatFloat(val, 'f', -1, 64))
case bool:
return f.sprintColor(f.BoolColor, strconv.FormatBool(val))
case nil:
return f.sprintColor(f.NullColor, "null")
case map[string]interface{}:
return f.processMap(val, depth)
case []interface{}:
return f.processArray(val, depth)
}
return ""
}
func (f *Formatter) processString(s string) string {
r := []rune(s)
if f.StringMaxLength != 0 && len(r) >= f.StringMaxLength {
s = string(r[0:f.StringMaxLength]) + "..."
}
buf := &bytes.Buffer{}
encoder := json.NewEncoder(buf)
encoder.SetEscapeHTML(false)
encoder.Encode(s)
s = string(buf.Bytes())
s = strings.TrimSuffix(s, "\n")
return f.sprintColor(f.StringColor, s)
}
func (f *Formatter) processMap(m map[string]interface{}, depth int) string {
if len(m) == 0 {
return "{}"
}
currentIndent := f.generateIndent(depth - 1)
nextIndent := f.generateIndent(depth)
rows := []string{}
keys := []string{}
for key := range m {
keys = append(keys, key)
}
sort.Strings(keys)
for _, key := range keys {
val := m[key]
k := f.sprintfColor(f.KeyColor, `"%s"`, key)
v := f.pretty(val, depth+1)
valueIndent := " "
if f.Newline == "" {
valueIndent = ""
}
row := fmt.Sprintf("%s%s:%s%s", nextIndent, k, valueIndent, v)
rows = append(rows, row)
}
return fmt.Sprintf("{%s%s%s%s}", f.Newline, strings.Join(rows, ","+f.Newline), f.Newline, currentIndent)
}
func (f *Formatter) processArray(a []interface{}, depth int) string {
if len(a) == 0 {
return "[]"
}
currentIndent := f.generateIndent(depth - 1)
nextIndent := f.generateIndent(depth)
rows := []string{}
for _, val := range a {
c := f.pretty(val, depth+1)
row := nextIndent + c
rows = append(rows, row)
}
return fmt.Sprintf("[%s%s%s%s]", f.Newline, strings.Join(rows, ","+f.Newline), f.Newline, currentIndent)
}
func (f *Formatter) generateIndent(depth int) string {
return strings.Repeat(" ", f.Indent*depth)
}
// Marshal JSON data with default options.
func Marshal(v interface{}) ([]byte, error) {
return NewFormatter().Marshal(v)
}
// Format JSON string with default options.
func Format(data []byte) ([]byte, error) {
return NewFormatter().Format(data)
}