1 package colorjson
2
3 import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "sort"
8 "strconv"
9 "strings"
10
11 "github.com/fatih/color"
12 )
13
14 const initialDepth = 0
15 const valueSep = ","
16 const null = "null"
17 const startMap = "{"
18 const endMap = "}"
19 const startArray = "["
20 const endArray = "]"
21
22 const emptyMap = startMap + endMap
23 const emptyArray = startArray + endArray
24
25 type Formatter struct {
26 KeyColor *color.Color
27 StringColor *color.Color
28 BoolColor *color.Color
29 NumberColor *color.Color
30 NullColor *color.Color
31 StringMaxLength int
32 Indent int
33 DisabledColor bool
34 RawStrings bool
35 }
36
37 func NewFormatter() *Formatter {
38 return &Formatter{
39 KeyColor: color.New(color.FgWhite),
40 StringColor: color.New(color.FgGreen),
41 BoolColor: color.New(color.FgYellow),
42 NumberColor: color.New(color.FgCyan),
43 NullColor: color.New(color.FgMagenta),
44 StringMaxLength: 0,
45 DisabledColor: false,
46 Indent: 0,
47 RawStrings: false,
48 }
49 }
50
51 func (f *Formatter) sprintfColor(c *color.Color, format string, args ...interface{}) string {
52 if f.DisabledColor || c == nil {
53 return fmt.Sprintf(format, args...)
54 }
55 return c.SprintfFunc()(format, args...)
56 }
57
58 func (f *Formatter) sprintColor(c *color.Color, s string) string {
59 if f.DisabledColor || c == nil {
60 return fmt.Sprint(s)
61 }
62 return c.SprintFunc()(s)
63 }
64
65 func (f *Formatter) writeIndent(buf *bytes.Buffer, depth int) {
66 buf.WriteString(strings.Repeat(" ", f.Indent*depth))
67 }
68
69 func (f *Formatter) writeObjSep(buf *bytes.Buffer) {
70 if f.Indent != 0 {
71 buf.WriteByte('\n')
72 } else {
73 buf.WriteByte(' ')
74 }
75 }
76
77 func (f *Formatter) Marshal(jsonObj interface{}) ([]byte, error) {
78 buffer := bytes.Buffer{}
79 f.marshalValue(jsonObj, &buffer, initialDepth)
80 return buffer.Bytes(), nil
81 }
82
83 func (f *Formatter) marshalMap(m map[string]interface{}, buf *bytes.Buffer, depth int) {
84 remaining := len(m)
85
86 if remaining == 0 {
87 buf.WriteString(emptyMap)
88 return
89 }
90
91 keys := make([]string, 0)
92 for key := range m {
93 keys = append(keys, key)
94 }
95
96 sort.Strings(keys)
97
98 buf.WriteString(startMap)
99 f.writeObjSep(buf)
100
101 for _, key := range keys {
102 f.writeIndent(buf, depth+1)
103 buf.WriteString(f.KeyColor.Sprintf("\"%s\": ", key))
104 f.marshalValue(m[key], buf, depth+1)
105 remaining--
106 if remaining != 0 {
107 buf.WriteString(valueSep)
108 }
109 f.writeObjSep(buf)
110 }
111 f.writeIndent(buf, depth)
112 buf.WriteString(endMap)
113 }
114
115 func (f *Formatter) marshalArray(a []interface{}, buf *bytes.Buffer, depth int) {
116 if len(a) == 0 {
117 buf.WriteString(emptyArray)
118 return
119 }
120
121 buf.WriteString(startArray)
122 f.writeObjSep(buf)
123
124 for i, v := range a {
125 f.writeIndent(buf, depth+1)
126 f.marshalValue(v, buf, depth+1)
127 if i < len(a)-1 {
128 buf.WriteString(valueSep)
129 }
130 f.writeObjSep(buf)
131 }
132 f.writeIndent(buf, depth)
133 buf.WriteString(endArray)
134 }
135
136 func (f *Formatter) marshalValue(val interface{}, buf *bytes.Buffer, depth int) {
137 switch v := val.(type) {
138 case map[string]interface{}:
139 f.marshalMap(v, buf, depth)
140 case []interface{}:
141 f.marshalArray(v, buf, depth)
142 case string:
143 f.marshalString(v, buf)
144 case float64:
145 buf.WriteString(f.sprintColor(f.NumberColor, strconv.FormatFloat(v, 'f', -1, 64)))
146 case bool:
147 buf.WriteString(f.sprintColor(f.BoolColor, (strconv.FormatBool(v))))
148 case nil:
149 buf.WriteString(f.sprintColor(f.NullColor, null))
150 case json.Number:
151 buf.WriteString(f.sprintColor(f.NumberColor, v.String()))
152 }
153 }
154
155 func (f *Formatter) marshalString(str string, buf *bytes.Buffer) {
156 if !f.RawStrings {
157 strBytes, _ := json.Marshal(str)
158 str = string(strBytes)
159 }
160
161 if f.StringMaxLength != 0 && len(str) >= f.StringMaxLength {
162 str = fmt.Sprintf("%s...", str[0:f.StringMaxLength])
163 }
164
165 buf.WriteString(f.sprintColor(f.StringColor, str))
166 }
167
168
169 func Marshal(jsonObj interface{}) ([]byte, error) {
170 return NewFormatter().Marshal(jsonObj)
171 }
172
View as plain text