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  }