github.com/philpearl/plenc@v0.0.15/plenccodec/output.go (about) 1 package plenccodec 2 3 import ( 4 "strconv" 5 "time" 6 ) 7 8 // Outputter is an interface that a Descriptor uses to turn plenc data to some 9 // other output 10 type Outputter interface { 11 StartObject() 12 EndObject() 13 StartArray() 14 EndArray() 15 NameField(name string) 16 17 Int64(v int64) 18 Uint64(v uint64) 19 Float64(v float64) 20 Float32(v float32) 21 String(v string) 22 Bool(v bool) 23 Time(t time.Time) 24 Raw(v string) 25 } 26 27 // JSONOutput converts Descriptor output to JSON. 28 type JSONOutput struct { 29 data []byte 30 depth int 31 inField bool 32 33 stack []stackEntry 34 } 35 36 type state int 37 38 const ( 39 stateValue state = iota 40 stateKey 41 stateObjValue 42 ) 43 44 type stackEntry struct { 45 state state 46 } 47 48 func (j *JSONOutput) Done() []byte { 49 j.end() 50 return j.data 51 } 52 53 func (j *JSONOutput) Reset() { 54 j.data = j.data[:0] 55 j.depth = 0 56 j.inField = false 57 j.stack = j.stack[:0] 58 } 59 60 func (j *JSONOutput) prefix() { 61 if j.inField { 62 j.inField = false 63 return 64 } 65 for i := 0; i < j.depth; i++ { 66 j.data = append(j.data, " "...) 67 } 68 } 69 70 func (j *JSONOutput) end() { 71 if j.depth == 0 { 72 j.data = append(j.data, '\n') 73 return 74 } 75 j.depth-- 76 j.stack = j.stack[:len(j.stack)-1] 77 l := len(j.data) 78 if l < 2 { 79 return 80 } 81 if j.data[l-2] == ',' && j.data[l-1] == '\n' { 82 j.data[l-2] = '\n' 83 j.data = j.data[:l-1] 84 } 85 } 86 87 func (j *JSONOutput) punctuate() { 88 if len(j.stack) == 0 { 89 return 90 } 91 s := &j.stack[len(j.stack)-1] 92 switch s.state { 93 case stateKey: 94 j.data = append(j.data, ": "...) 95 s.state = stateObjValue 96 case stateObjValue: 97 j.data = append(j.data, ",\n"...) 98 s.state = stateKey 99 case stateValue: 100 j.data = append(j.data, ",\n"...) 101 } 102 } 103 104 func (j *JSONOutput) StartObject() { 105 j.prefix() 106 j.data = append(j.data, "{\n"...) 107 j.depth++ 108 j.stack = append(j.stack, stackEntry{state: stateKey}) 109 } 110 111 func (j *JSONOutput) EndObject() { 112 j.end() 113 j.prefix() 114 j.data = append(j.data, '}') 115 j.punctuate() 116 } 117 118 func (j *JSONOutput) StartArray() { 119 j.prefix() 120 j.data = append(j.data, "[\n"...) 121 j.stack = append(j.stack, stackEntry{state: stateValue}) 122 j.depth++ 123 } 124 125 func (j *JSONOutput) EndArray() { 126 j.end() 127 j.prefix() 128 j.data = append(j.data, ']') 129 j.punctuate() 130 } 131 132 func (j *JSONOutput) NameField(name string) { 133 j.prefix() 134 j.inField = true 135 j.data = j.appendString(j.data, name) 136 j.punctuate() 137 } 138 139 func (j *JSONOutput) Int64(v int64) { 140 j.prefix() 141 j.data = strconv.AppendInt(j.data, v, 10) 142 j.punctuate() 143 } 144 145 func (j *JSONOutput) Uint64(v uint64) { 146 j.prefix() 147 j.data = strconv.AppendUint(j.data, v, 10) 148 j.punctuate() 149 } 150 151 func (j *JSONOutput) Float64(v float64) { 152 j.prefix() 153 j.data = strconv.AppendFloat(j.data, v, 'g', -1, 64) 154 j.punctuate() 155 } 156 157 func (j *JSONOutput) Float32(v float32) { 158 j.prefix() 159 j.data = strconv.AppendFloat(j.data, float64(v), 'g', -1, 64) 160 j.punctuate() 161 } 162 163 func (j *JSONOutput) String(v string) { 164 j.prefix() 165 j.data = j.appendString(j.data, v) 166 j.punctuate() 167 } 168 169 func (j *JSONOutput) Raw(v string) { 170 j.prefix() 171 j.data = append(j.data, v...) 172 j.punctuate() 173 } 174 175 func (j *JSONOutput) Bool(v bool) { 176 j.prefix() 177 j.data = strconv.AppendBool(j.data, v) 178 j.punctuate() 179 } 180 181 func (j *JSONOutput) Time(t time.Time) { 182 j.prefix() 183 j.data = t.AppendFormat(j.data, `"`+time.RFC3339Nano+`"`) 184 j.punctuate() 185 } 186 187 const hex = "0123456789abcdef" 188 189 func (j *JSONOutput) appendString(data []byte, v string) []byte { 190 data = append(data, '"') 191 for i := 0; i < len(v); i++ { 192 c := v[i] 193 switch c { 194 case '\\', '"': 195 data = append(data, '\\', c) 196 case '\n': 197 data = append(data, '\\', 'n') 198 case '\r': 199 data = append(data, '\\', 'r') 200 case '\t': 201 data = append(data, '\\', 't') 202 default: 203 if c < 32 { 204 data = append(data, '\\', 'u', '0', '0', hex[c>>4], hex[c&0xF]) 205 } else { 206 // append in its natural form 207 data = append(data, c) 208 } 209 } 210 } 211 data = append(data, '"') 212 return data 213 }