tlog.app/go/tlog@v0.23.1/convert/json.go (about) 1 package convert 2 3 import ( 4 "encoding/base64" 5 "errors" 6 "io" 7 "math" 8 "path/filepath" 9 "strconv" 10 "time" 11 12 "github.com/nikandfor/hacked/hfmt" 13 "github.com/nikandfor/hacked/low" 14 "tlog.app/go/loc" 15 16 "tlog.app/go/tlog" 17 tlow "tlog.app/go/tlog/low" 18 "tlog.app/go/tlog/tlwire" 19 ) 20 21 type ( 22 JSON struct { 23 io.Writer 24 25 AppendNewLine bool 26 AppendKeySafe bool 27 FloatInfNaN bool 28 TimeFormat string 29 TimeZone *time.Location 30 31 Rename RenameFunc 32 33 d tlwire.Decoder 34 35 b low.Buf 36 } 37 38 RenameFunc func(b, p, k []byte, st int) ([]byte, bool) 39 40 TagSub struct { 41 Tag byte 42 Sub int64 43 } 44 45 SimpleRenameRule struct { 46 Tags []TagSub 47 Key string 48 } 49 50 SimpleRenamer struct { 51 tlwire.Decoder 52 53 Rules map[string]SimpleRenameRule 54 55 Fallback RenameFunc 56 } 57 ) 58 59 func NewJSON(w io.Writer) *JSON { 60 return &JSON{ 61 Writer: w, 62 AppendNewLine: true, 63 AppendKeySafe: true, 64 FloatInfNaN: false, 65 TimeFormat: time.RFC3339Nano, 66 TimeZone: time.Local, 67 } 68 } 69 70 func (w *JSON) Write(p []byte) (i int, err error) { 71 b := w.b[:0] 72 73 more: 74 tag, els, i := w.d.Tag(p, i) 75 if tag != tlwire.Map { 76 return i, errors.New("map expected") 77 } 78 79 b = append(b, '{') 80 81 var k []byte 82 for el := 0; els == -1 || el < int(els); el++ { 83 if els == -1 && w.d.Break(p, &i) { 84 break 85 } 86 87 if el != 0 { 88 b = append(b, ',') 89 } 90 91 b = append(b, '"') 92 93 k, i = w.d.Bytes(p, i) 94 95 var renamed bool 96 97 if w.Rename != nil { 98 b, renamed = w.Rename(b, p, k, i) 99 } 100 101 if !renamed { 102 if w.AppendKeySafe { 103 b = tlow.AppendSafe(b, k) 104 } else { 105 b = append(b, k...) 106 } 107 } 108 109 b = append(b, '"', ':') 110 111 b, i = w.ConvertValue(b, p, i) 112 } 113 114 b = append(b, '}') 115 if w.AppendNewLine { 116 b = append(b, '\n') 117 } 118 119 if i < len(p) { 120 goto more 121 } 122 123 w.b = b[:0] 124 125 _, err = w.Writer.Write(b) 126 if err != nil { 127 return 0, err 128 } 129 130 return len(p), nil 131 } 132 133 func (w *JSON) ConvertKey(b, p []byte, st int) (_ []byte, i int) { 134 tag := w.d.TagOnly(p, st) 135 136 b = append(b, '"') 137 138 switch tag { 139 case tlwire.Int, tlwire.Neg, 140 tlwire.Special: 141 b, i = w.ConvertValue(b, p, st) 142 case tlwire.Bytes, tlwire.String: 143 var k []byte 144 k, i = w.d.Bytes(p, st) 145 146 if w.AppendKeySafe { 147 b = tlow.AppendSafe(b, k) 148 } else { 149 b = append(b, k...) 150 } 151 default: 152 b = hfmt.Appendf(b, `UNSUPPORTED_KEY_TYPE_%x`, tag) 153 i = w.d.Skip(p, st) 154 } 155 156 b = append(b, '"') 157 158 return b, i 159 } 160 161 func (w *JSON) ConvertValue(b, p []byte, st int) (_ []byte, i int) { 162 tag, sub, i := w.d.Tag(p, st) 163 164 switch tag { 165 case tlwire.Int: 166 b = strconv.AppendUint(b, uint64(sub), 10) 167 case tlwire.Neg: 168 b = strconv.AppendInt(b, 1-sub, 10) 169 case tlwire.Bytes: 170 b = append(b, '"') 171 172 m := base64.StdEncoding.EncodedLen(int(sub)) 173 bst := len(b) 174 175 for cap(b) < bst+m { 176 b = append(b[:cap(b)], 0, 0, 0, 0) 177 } 178 179 b = b[:bst+m] 180 181 base64.StdEncoding.Encode(b[bst:], p[i:i+int(sub)]) 182 183 b = append(b, '"') 184 185 i += int(sub) 186 case tlwire.String: 187 b = append(b, '"') 188 189 b = tlow.AppendSafe(b, p[i:i+int(sub)]) 190 191 b = append(b, '"') 192 193 i += int(sub) 194 case tlwire.Array: 195 b = append(b, '[') 196 197 for el := 0; sub == -1 || el < int(sub); el++ { 198 if sub == -1 && w.d.Break(p, &i) { 199 break 200 } 201 202 if el != 0 { 203 b = append(b, ',') 204 } 205 206 b, i = w.ConvertValue(b, p, i) 207 } 208 209 b = append(b, ']') 210 case tlwire.Map: 211 b = append(b, '{') 212 213 for el := 0; sub == -1 || el < int(sub); el++ { 214 if sub == -1 && w.d.Break(p, &i) { 215 break 216 } 217 218 if el != 0 { 219 b = append(b, ',') 220 } 221 222 b, i = w.ConvertKey(b, p, i) 223 224 b = append(b, ':') 225 226 b, i = w.ConvertValue(b, p, i) 227 } 228 229 b = append(b, '}') 230 case tlwire.Semantic: 231 switch sub { 232 case tlwire.Time: 233 var t time.Time 234 t, i = w.d.Time(p, st) 235 236 if w.TimeZone != nil { 237 t = t.In(w.TimeZone) 238 } 239 240 if w.TimeFormat != "" { 241 b = append(b, '"') 242 b = t.AppendFormat(b, w.TimeFormat) 243 b = append(b, '"') 244 } else { 245 b = strconv.AppendInt(b, t.UnixNano(), 10) 246 } 247 case tlog.WireID: 248 var id tlog.ID 249 i = id.TlogParse(p, st) 250 251 bst := len(b) + 1 252 b = append(b, `"12345678-9_12-3456-789_-123456789_12"`...) 253 254 id.FormatTo(b, bst, 'u') 255 case tlwire.Caller: 256 var pc loc.PC 257 var pcs loc.PCs 258 pc, pcs, i = w.d.Callers(p, st) 259 260 if pcs != nil { 261 b = append(b, '[') 262 for i, pc := range pcs { 263 if i != 0 { 264 b = append(b, ',') 265 } 266 267 _, file, line := pc.NameFileLine() 268 b = hfmt.Appendf(b, `"%v:%d"`, filepath.Base(file), line) 269 } 270 b = append(b, ']') 271 } else { 272 _, file, line := pc.NameFileLine() 273 274 b = hfmt.Appendf(b, `"%v:%d"`, filepath.Base(file), line) 275 } 276 default: 277 b, i = w.ConvertValue(b, p, i) 278 } 279 case tlwire.Special: 280 switch sub { 281 case tlwire.False: 282 b = append(b, "false"...) 283 case tlwire.True: 284 b = append(b, "true"...) 285 case tlwire.Nil, tlwire.Undefined, tlwire.None, tlwire.Hidden, tlwire.SelfRef: 286 b = append(b, "null"...) 287 case tlwire.Float64, tlwire.Float32, tlwire.Float16, tlwire.Float8: 288 var f float64 289 f, i = w.d.Float(p, st) 290 291 switch { 292 case !w.FloatInfNaN && math.IsNaN(f): 293 b = append(b, `"NaN"`...) 294 case !w.FloatInfNaN && math.IsInf(f, 1): 295 b = append(b, `"+Inf"`...) 296 case !w.FloatInfNaN && math.IsInf(f, -1): 297 b = append(b, `"-Inf"`...) 298 default: 299 b = strconv.AppendFloat(b, f, 'f', -1, 64) 300 } 301 302 default: 303 panic(sub) 304 } 305 } 306 307 return b, i 308 } 309 310 func (r SimpleRenamer) Rename(b, p, k []byte, i int) ([]byte, bool) { 311 rule, ok := r.Rules[string(k)] 312 if !ok { 313 return r.fallback(b, p, k, i) 314 } 315 316 for _, ts := range rule.Tags { 317 tag, sub, j := r.Tag(p, i) 318 319 if tag != tlwire.Semantic && tag != tlwire.Special { 320 sub = 0 321 } 322 323 if ts != (TagSub{tag, sub}) { 324 return r.fallback(b, p, k, i) 325 } 326 327 i = j 328 } 329 330 return append(b, rule.Key...), true 331 } 332 333 func (r SimpleRenamer) fallback(b, p, k []byte, i int) ([]byte, bool) { 334 if r.Fallback == nil { 335 return b, false 336 } 337 338 return r.Fallback(b, p, k, i) 339 }